Как защитить API с помощью edge — Versola Docs
/docs
До 1.00.7.5versola.ioGitHub

Как защитить API с помощью edge

Поставьте внутренний API за edge. Зарегистрируйте ресурс, выдайте разрешения через роль, добавьте пресет авторизации для входа в браузере, CEL-правило и проверьте через curl, включая ответ 403.

В этом руководстве один upstream API ставится за edge, и браузерное приложение вызывает его с cookie сессии. В конце пользователь читает свои заказы, не может читать чужие, а у вас есть команды curl, которые это доказывают. Нужна работающая установка с тенантом, привязанным к edge, и ваш API, доступный для edge по сети.

В примере используются эти имена. Замените их своими.

ЧтоЗначение
Публичный адрес edge (edge-url)https://edge.example.com
Публичный адрес authhttps://id.example.com
Ваше браузерное приложениеhttps://app.example.com
Upstream APIhttp://orders.internal.example.com:8080
ID ресурсаorders-api
Клиентacme-web
Разрешение, рольorders:read, customer
Пресетweb

Предварительные условия

  • Установка с central, auth и edge (Запуск Versola локально).
  • Тенант с нужным профилем, привязанный к вашему edge в разделе Tenants. См. Профили безопасности.
  • В central настроен бэкенд УЦ для клиентских сертификатов, потому что веб-клиент за edge получает выпущенный сертификат (Клиентские сертификаты edge). Цели CLI настраивают его сами.
  • Upstream API с двумя маршрутами: GET /orders и GET /users/{userId}/orders.

1. Зарегистрируйте ресурс

  1. Откройте Resources и нажмите + Create Resource.

  2. Задайте Resource ID orders-api и Absolute resource URI http://orders.internal.example.com:8080. В URI нет пути, query и fragment.

  3. Выберите тип Internal. Тогда edge вызывает ваш API с HTTP Basic и никогда не пересылает токен пользователя.

  4. В Audience отметьте клиента acme-web, когда он появится (шаг 3). К этой карточке можно вернуться позже.

  5. Нажмите Create resource и скопируйте секрет, который покажет баннер. Он показывается один раз. Настройте ваш API на Basic-аутентификацию: пользователь orders-api, пароль — этот секрет. Подробности в Как зарегистрировать ресурс.

  6. На карточке Endpoints нажмите Edit endpoints, затем Add и добавьте GET /orders. Правила оставьте пустыми.

  7. Снова нажмите Add и добавьте GET /users/{userId}/orders. В Allow (CEL) введите:

    request.path.params.userId == token.sub
  8. Нажмите Save endpoints.

Правило сравнивает userId из URL с claim sub токена вызывающего. Остальные поля и переменные описаны в разделе Политики эндпоинтов.

Панели эндпоинта. Правило из руководства вводится в Allow (CEL)
Endpoint ресурса с Allow, step-up и правилом Inject.

2. Выдайте доступ через разрешение и роль

Пока эндпоинт не покрыт разрешением, он отвечает 403.

  1. Откройте Permissions, создайте orders:read и привяжите оба эндпоинта orders-api (Как зарегистрировать разрешение).
  2. Откройте Roles, создайте customer и добавьте в нее orders:read (Как зарегистрировать роль).
  3. Откройте Users и назначьте тестовому пользователю роль customer.

3. Создайте клиента и пресет авторизации

  1. Откройте Clients и запустите мастер создания. На первом шаге выберите Web app и задайте ID клиента acme-web.

  2. Добавьте redirect URI https://edge.example.com/complete. Это маршрут /complete в edge, который принимает код от auth.

  3. Завершите мастер и вернитесь в поле Audience ресурса, чтобы добавить acme-web, если вы пропустили это раньше (Как зарегистрировать клиента).

  4. В клиенте откройте Authorization Presets и нажмите + Add Preset:

    ПолеЗначение
    Preset IDweb
    Redirect URIhttps://edge.example.com/complete
    Scopesopenid
    Response Typecode
    Post-login redirect URIhttps://app.example.com/
    Post-logout redirect URI (optional)https://app.example.com/logged-out
    Session cookie domain, path (optional)оставьте пустыми: cookie на хосте edge, путь /
  5. Нажмите Save Presets. edge загружает пресеты вместе с остальной конфигурацией, так что подождите до configuration-cache-refresh-interval (по умолчанию генерируется 5 минут, локально 1 минута).

4. Направьте браузерное приложение на edge

  • Вход. Отправьте браузер на https://edge.example.com/login/web. После входа он вернется на https://app.example.com/ и будет держать cookie EDGE_SESSION, которую JavaScript прочитать не может. Чтобы потребовать свежий или более сильный вход, добавьте в URL acr_values или max_age.
  • Вызов API. Запрашивайте https://edge.example.com/resources/orders-api/orders с credentials: 'include'. Часть после /resources/orders-api сопоставляется с зарегистрированными эндпоинтами.
  • Один сайт, один origin. Cookie имеет SameSite=Strict, поэтому приложение и edge должны быть на одном сайте. edge не отправляет заголовки CORS, поэтому отдавайте приложение и edge с одного origin, например маршрутизируйте /resources/* и /login/* с прокси вашего сайта на edge.
  • Истекшая сессия. Если cookie нельзя обновить, edge отвечает 401 с заголовком Location: /login/web. Перейдите по нему.
  • Выход. Отправьте браузер на https://edge.example.com/logout/web.

5. Проверьте через curl

Войдите в браузере и скопируйте значение cookie EDGE_SESSION из инструментов разработчика. Оно имеет вид web:<access token>. ID пользователя, то есть claim sub, возьмите из токена или из раздела Users. Токен, привязанный к сертификату edge, работает только из cookie, поэтому отправляйте его как cookie, а не как Authorization: Bearer.

Прочитайте свои заказы:

curl -i -H "Cookie: EDGE_SESSION=web:<access token>" \
  https://edge.example.com/resources/orders-api/users/<your-user-id>/orders
HTTP/1.1 200 OK
...

Тело — то, что вернет ваш API. Теперь запросите чужие заказы:

curl -i -H "Cookie: EDGE_SESSION=web:<access token>" \
  https://edge.example.com/resources/orders-api/users/<another-user-id>/orders
HTTP/1.1 403 Forbidden

Правило вернуло false. edge пишет в лог access_rule_denied для этого запроса и не отправляет тело. Upstream вызова не видел. Без учетных данных:

curl -i https://edge.example.com/resources/orders-api/orders
HTTP/1.1 401 Unauthorized

Путь, под который не подходит ни один эндпоинт, при действующей сессии дает 404.

DPoP или токены, привязанные к сертификату

Шаги выше обслуживают браузер. Если вызывающий — другая система (банк, бэкенд партнера, мобильное приложение), он присылает собственный токен в заголовке Authorization, и вы выбираете, как этот токен привязан к вызывающему. edge решает по claim cnf (Токены с привязкой к отправителю).

DPoP (cnf.jkt)Привязка к сертификату (cnf.x5t#S256)
Что доказывает вызывающийВладение ключом, подписанным proof в каждом запросеВладение клиентским сертификатом, в TLS-handshake
ПортОбычный порт edgeMutual-TLS listener edge, MPORT
ЗаголовокAuthorization: DPoP и DPoPAuthorization: Bearer
Где живет ключВ приложении или в хранилище ключей устройстваВ сертификате, выпущенном УЦ, с продлением
Что нужно вызывающемуРеализация DPoP, повтор запроса с nonce, если он требуетсяHTTP-клиент с mTLS и план продления сертификата
Что запускаете выНичего лишнего. Блок dpop в edgeБлок mutual-tls с серверным сертификатом, УЦ вызывающего в trusted-certificates, а если у эндпоинтов включен fetchUserInfo, то блок native в edge и edge-trusted-certificates в auth
Токен после продления у вызывающегоНе затронут, пока ключ тот жеТокен назван на старый сертификат. Вызывающий получает новый токен с новым сертификатом
Типичный вызывающийМобильные и десктопные приложения, сервисы и скриптыБанки и партнеры по FAPI 2.0, бэкенды, у которых уже есть клиентский сертификат

Выбирайте DPoP, если вызывающий не умеет mutual TLS или вам не нужен второй публичный порт. Выбирайте токены, привязанные к сертификату, если вызывающий — регулируемый бэкенд, который уже аутентифицируется сертификатом (tls_client_auth), если этого требуют правила вашей отрасли или если вы хотите, чтобы доказательство было в транспорте, а не в каждом запросе. Токен, привязанный и к ключу, и к сертификату, edge отклоняет при любом предъявлении, поэтому выбирайте для клиента одну привязку.

Чтобы обслуживать вызывающего с сертификатом:

  1. Зарегистрируйте клиента с authMethod: tls_client_auth (Как зарегистрировать клиента). Его токены будут привязаны к сертификату.
  2. Убедитесь, что у тенанта настроен путь клиентского сертификата (Клиентские сертификаты и их продление).
  3. Добавьте в конфигурацию edge блок mutual-tls с УЦ клиентского сертификата в trusted-certificates (Конфигурация edge).
  4. Если у эндпоинта включено Fetch userinfo, дайте edge блок native, а в auth задайте mutual-tls.edge-trusted-certificates с УЦ edge.
  5. Вызывающий запрашивает токен в auth со своим сертификатом и вызывает https://<адрес mutual-TLS>/resources/orders-api/orders с Authorization: Bearer <access token> и тем же сертификатом.
curl -i --cert bank.crt --key bank.key \
  -H "Authorization: Bearer <access token>" \
  https://api.example.com:8083/resources/orders-api/orders

Тот же токен на обычном порту получает 401, а в журнале certificate_bound_token_outside_session.

Если не работает

Что вы видитеЧто проверить
404 на /login/webПресет не сохранен или edge еще не перечитал конфигурацию. Подождите один интервал обновления
403 на каждый вызовУ роли нет разрешения или разрешение не привязано к эндпоинту. Код permission_denied в логе edge это подтверждает. Изменения ролей и разрешений тоже доходят до одного интервала обновления
403 с audience_denied в логеКлиента нет в Audience ресурса
403 на ваши собственные заказыtoken.sub — не тот ID пользователя, что в URL
500Правило не вычисляется. Ищите access_rule_failed в логе
Ваш API отвечает 401Он не принимает Basic-учетные данные orders-api:<secret>

Что дальше