Вызов API с токеном, привязанным к сертификату — Versola Docs
/docs
До 1.00.7.5versola.ioGitHub

Вызов API с токеном, привязанным к сертификату

Необязательный mutual-TLS порт edge для /resources. Когда принимается токен, привязанный к сертификату вызывающего (cnf.x5t#S256), как edge получает userinfo из auth, блок конфигурации и что по-прежнему отклоняет обычный порт.

API-клиент, например банк, может держать access-токен, привязанный к его собственному клиентскому сертификату (RFC 8705 §3). На обычном порту edge сертификат TLS-соединения не виден, поэтому такой токен там отклоняется. edge может запустить второй listener, который сам терминирует mutual TLS, читает сертификат вызывающего из handshake и принимает токен от владельца этого сертификата. На этой странице описано, как это работает и как включить. Нужны работающий edge (Обзор edge), клиент, зарегистрированный с tls_client_auth и токенами, привязанными к сертификату (Клиентские сертификаты и их продление), и серверный сертификат для публичного имени, по которому обращаются вызывающие.

Что делает listener

  • Обслуживает /resources/{resourceId}/{path} и ничего больше. /login, /logout, /native, /permissions/me и остальное остаются на обычном порту.
  • Требует клиентский сертификат в handshake. Вызывающий без сертификата или с сертификатом, который не строится к trusted-certificates, до HTTP-уровня не доходит.
  • Остальной конвейер тот же, что на обычном порту: отзыв, поиск ресурса и эндпоинта, разрешение, audience, fetchUserInfo, правило allow, step-up, подстановка, пересылка. См. Политики эндпоинтов.

Когда токен принимается

Решает claim cnf токена, как и везде в edge. На этом listener’е:

ТокенПредъявлен какРезультат
cnf.x5t#S256Authorization: Bearer, SHA-256-отпечаток сертификата равен claimПринят
cnf.x5t#S256Authorization: Bearer, любой другой сертификат, даже из доверенных listener’у401, certificate_bound_token_outside_session
cnf.x5t#S256тот же токен на обычном порту401, certificate_bound_token_outside_session
cnf.jktAuthorization: BearerОтклонен (dpop_downgrade)
cnf.jkt и cnf.x5t#S256любой способОтклонен. Токен, привязанный и к ключу, и к сертификату, отклоняется при любом предъявлении
без cnfAuthorization: BearerПринят. Сертификат все равно нужен для соединения, но токен к нему ничем не привязан

Сравнивается SHA-256 от DER-кодировки сертификата в base64url с claim, сравнение выполняется за постоянное время. Доверия к издателю недостаточно: сертификат доверенного УЦ, но не тот, что назван в токене, отклоняется.

Запрос со схемой DPoP на этом listener’е проверяется на htu по edge-url; собственный адрес listener’а в качестве htu пока не принимается. Токены, привязанные к DPoP, отправляйте на обычный порт.

Как включить

Listener необязателен. Без блока mutual-tls edge второй порт не открывает. Добавьте блок в том же виде, что и у listener’а auth:

mutual-tls {
  certificate          = "/certs/edge-mtls-server.crt"  # что предъявляет listener
  private-key          = "/certs/edge-mtls-server.key"  # PKCS#8 без шифрования
  trusted-certificates = "/certs/callers-ca.crt"        # УЦ, выпускающие сертификаты ваших вызывающих
  external-url         = "https://api.example.com:8083"
}
КлючОбязателенЗначение
certificateдаПуть к PEM с серверным сертификатом для публичного имени, которым пользуются вызывающие
private-keyдаПуть к PEM с его ключом, PKCS#8 без шифрования
trusted-certificatesдаПуть к PEM с удостоверяющими центрами, по которым проверяется цепочка вызывающего. Значения по умолчанию нет: иначе за ваших вызывающих ручался бы каждый публичный УЦ
external-urlдаАдрес, которым пользуются вызывающие. Для вашей документации и инструментов: развертывание за NAT или TCP-балансировщиком слушает один адрес, а публикует другой

Порт — не ключ. Он берется из MPORT (по умолчанию 8083), адрес привязки — из MBIND_HOST. Listener работает с обязательным клиентским сертификатом, режима с необязательным сертификатом нет.

Этот блок пишет только таргет разработки local. versola configure для vps и docker-local и Helm-чарт его не пишут и не выпускают для него серверный сертификат: оба вы предоставляете сами.

УЦ, выпустивший сертификат вызывающего, должен быть в trusted-certificates здесь, и обычно это тот же УЦ, что стоит в mutual-tls.trusted-certificates у auth для этого клиента.

Как edge получает claims пользователя

Токен вызывающего, привязанный к сертификату, проверяется на /userinfo в auth так же строго, как на прокси. У edge нет ни закрытого ключа вызывающего, ни способа передать его сертификат в auth. Поэтому для эндпоинта с включенным fetchUserInfo edge обращается к auth с собственным сертификатом, тем самым, который он предъявляет central, на mutual-TLS listener’е auth (native.auth-mutual-tls-url). auth обслуживает такой вызов без проверок DPoP и сертификата владельца, только если сертификат принадлежит edge и выпущен выделенным УЦ edge. Правило и его цена как границы безопасности описаны в разделе Токены с привязкой к отправителю.

Два следствия:

  • edge нужен блок native, даже если нативных приложений нет: в нем хранятся адрес и пины listener’а auth. Без него эндпоинт с fetchUserInfo отвечает 500 для токена, привязанного к сертификату. См. Конфигурация edge.
  • В auth нужен mutual-tls.edge-trusted-certificates. Без него ни один сертификат не считается сертификатом edge, и /userinfo вызов отклоняет.

Эндпоинты, у которых fetchUserInfo выключен, /userinfo не вызывают вовсе.

Безопасность

  • Закрытый ключ сертификата edge — самый чувствительный ключ развертывания. Тот, кто им владеет, может вызывать /userinfo как edge для любого токена без доказательства владения.
  • Тот, кто им владеет, может вызывать /userinfo как этот edge для любого токена тенантов, назначенных edge. auth доверяет сертификату edge только для edge, который central по-прежнему перечисляет, и только для назначенных ему тенантов.
  • Используйте отдельный УЦ для сертификатов edge. Никогда не тот, что выпускает клиентские сертификаты: УЦ, умеющий подписать urn:versola:edge:<id> для клиента, мог бы позволить этому клиенту выдать себя за edge. Поэтому auth проверяет и издателя, а не только имя, и пишет предупреждение при запуске, если один УЦ совмещает обе роли.
  • Делайте сертификаты edge короткоживущими и ротируйте их: срок самого сертификата сократить нельзя. Пакет vps выпускает их на 30 дней, edge продлевает сам. Удаление edge в central или снятие назначения его тенантов отрезает его в auth после следующего обновления конфигурации (configuration-cache-refresh-interval), а не по истечении срока.
  • Открывайте mutual-TLS listener auth только из сети, в которой работают edge.
  • Сертификат из пробрасываемого заголовка никогда не считается сертификатом edge. Учитывается только сертификат TLS-сессии.

Эксплуатация

  • Обновляйте auth и edge вместе. Новый edge не шлет assertion, а новый auth его игнорирует. Прежний механизм assertion удален вместе с GET /configuration/edges/registry и метриками dpop_edge_assertion_*.
  • Мониторинг. Отказ на этом listener’е попадает в журнал, как любой другой запрос edge, с ключом error равным certificate_bound_token_outside_session для неверного сертификата. Неудавшийся handshake строки запроса не создает: смотрите журнал TLS или балансировщика.

Сбои

СимптомПричинаЧто делать
Handshake отклоненНет клиентского сертификата или он не строится к trusted-certificatesДобавьте УЦ вызывающего в файл и перезапустите edge
401 certificate_bound_token_outside_session на mutual-TLS портуТокен выдан на другой сертификат, не тот, что в handshake. Вызывающий продлил сертификат, а токен оставил старыйПолучите новый токен с текущим сертификатом. У нового сертификата новый отпечаток
401 certificate_bound_token_outside_session на обычном портуНе тот портИспользуйте адрес mutual-TLS
500 на эндпоинте с fetchUserInfoУ edge нет блока native, или в auth нет edge-trusted-certificates, или сертификат не от этого УЦСм. Как edge получает claims пользователя
/login отвечает 404 на mutual-TLS портуТак задуманоИспользуйте обычный порт

Что дальше