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#S256 | Authorization: Bearer, SHA-256-отпечаток сертификата равен claim | Принят |
cnf.x5t#S256 | Authorization: Bearer, любой другой сертификат, даже из доверенных listener’у | 401, certificate_bound_token_outside_session |
cnf.x5t#S256 | тот же токен на обычном порту | 401, certificate_bound_token_outside_session |
cnf.jkt | Authorization: Bearer | Отклонен (dpop_downgrade) |
cnf.jkt и cnf.x5t#S256 | любой способ | Отклонен. Токен, привязанный и к ключу, и к сертификату, отклоняется при любом предъявлении |
без cnf | Authorization: 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 порту | Так задумано | Используйте обычный порт |
Что дальше
- Как защитить API с помощью edge: выбор между DPoP и токенами, привязанными к сертификату.
- Клиентские сертификаты и их продление: как банк получает и ротирует сертификат.
- Конфигурация edge: все ключи.