Перейти к содержимому

Авторизация — кто что может и как вы это доказываете

Control Center отвечает на два вопроса об авторизации, и они по-настоящему разные. «Может ли этот человек вызвать эту операцию?» — это про людей, роли и репозитории. «Может ли этот агент вызвать этот эффект?» — это про действия, аргументы и радиус поражения; этим занимаются Ограничения.

Эта страница — про первый вопрос и про след, который оставляют оба.

Любое решение об авторизации на стороне человека проходит через одну функцию:

can(principal, permission, resource) → allow | deny + why

Разрешение — это <domain>:<tier>: tickets:write, members:administer, repos:read. Каталог выводится, а не пишется вручную: он вычисляется из самого набора операций (префикс имени операции задаёт домен, минимальная роль, которую она уже требует, — уровень), поэтому все ~550 операций получают разрешение, и никому не приходится вписывать его руками.

Именно это свойство и стоит защищать. Благодаря ему у НОВОЙ операции проверка прав включается по умолчанию — слабое место модели с ручными аннотациями в том, что кто-то забывает, а забывают как раз проверку. Ratchet-тест фиксирует получившийся набор доменов, поэтому добавление операции с новым префиксом — это осознанное решение человека об именовании, а не случайность.

Четыре уровня точно соответствуют встроенным ролям:

Уровень Минимальная роль Пример
read гость tickets:read
write участник tickets:write
administer администратор members:administer
own владелец workspace:own

Пять встроенных ролей — владелец, администратор, участник, наблюдатель, гость — это готовые наборы поверх этого каталога. Владение — одно место на рабочее пространство, и передаётся оно явно, через Передать владение (иначе уходящий владелец неудаляем: депровизионинг через SCIM отказывается удалять владельца рабочего пространства — сначала передайте владение).

Пользовательские роли работают на вычитание. Такая роль указывает базовый набор (admin, member, viewer или guest — но никогда owner) и набор разрешений, УДАЛЯЕМЫХ из него, — «администратор, но без управления участниками». Она никогда не даёт больше, чем её основа, и именно поэтому их безопасно вводить в уже существующую установку: все существующие проверки остаются корректной верхней границей. У участников они хранятся как custom:<id>, а клиент, слишком старый, чтобы понять это значение, откатывается к гостю, а не угадывает.

Под ролями лежат права на уровне репозитория (none / read / review / write): членство в рабочем пространстве никогда молча не даёт больше прав, чем хост кода. Их проверяет каждая поверхность, открывающая доступ к коду, — включая те, что дотягиваются до кода сбоку, например терминал или сессия редактора, открытая на worktree пространства.

Некоторые операции вообще не про рабочее пространство: настройки всей установки, конфигурация SSO, учётные данные провайдеров, управление моделями, резервные копии всей установки. Такие операции объявляют полномочия сервера, и их отклоняют всем, кроме оператора установки (записанного владельца сервера). Так сделано намеренно: личность оператора одна, и определение у неё одно.

Каждое решение об авторизации — и разрешение, и отказ — дописывается в журнал аудита соответствующего рабочего пространства, связанный хеш-цепочкой.

Журнал только отказов отвечает на вопрос «что мы заблокировали?», но не на вопрос «что этот агент на самом деле сделал и кто это разрешил?», а разбор инцидента начинается именно с него. Поэтому каждая запись хранит всю цепочку атрибуции: действующий субъект, человек, от имени которого он действовал, делегирование, через которое он пришёл, действие и его аргументы (в виде дайджеста), решение и правило, по которому оно принято.

Записи связаны в цепочку: хеш каждой покрывает предыдущую, поэтому правка, удаление или перестановка любой строки ломает все хеши после неё. Проверить цепочку заново пересчитывает всё и называет первое звено, которое не сходится. Журнал аудита, который нельзя проверить, — просто журнал; тот, который можно пересчитать, — доказательство.

Два следствия, о которых стоит знать:

  • Таблица аудита не подпадает под очистку по возрасту. Хеш-цепочку нельзя усечь из середины. Срок хранения реализован как экспорт с последующим усечением, и после усечения остаётся контрольная точка с конечным хешем удалённого сегмента, так что сохранившееся по-прежнему проверяется вплоть до самого начала.
  • Он идёт рядом с лентой активности, а не поверх неё. Лента — это то, что делали люди; журнал — то, что было разрешено и отклонено. Общий идентификатор корреляции позволяет прочитать один вызов в обоих.

Он экспортируется как JSON с разделителями-переводами строк и может потоково передаваться в SIEM (Splunk, Elastic, Sentinel — HTTP-эндпоинт с bearer-токеном). Потоковая передача по замыслу не даёт гарантий: недоступный SIEM ни в коем случае не должен блокировать вызов инструмента агентом, а надёжная проверяемая копия в любом случае лежит на диске.

Установка может задать максимальный срок жизни сеанса и тайм-аут простоя. И то и другое проверяется при проверке учётных данных устройства, через которую проходит каждый аутентифицированный канал: срок сеанса, который клиент мог бы проигнорировать, отвечает на анкету, а не на действия атакующего. Установка также может потребовать SSO — тогда ручная привязка новых устройств отключается (со страховкой от блокировки: пока нет ни одного работающего подключения SSO, запрос отклоняется).

  • Это не ACL на отдельные объекты. Разрешений на отдельный тикет или сообщение нет — и это намеренно. Команды на практике просят разделение на уровне репозиториев, и оно есть; общий доступ к отдельным объектам — комбинаторная поверхность, спроса на которую здесь не показано.
  • Это не движок политик, который настраивают на DSL. Словарь намеренно закрыт, поэтому всю политику можно отобразить, объяснить и перечислить.
  • Проверка идёт только на сервере. Клиентские проверки существуют лишь затем, чтобы не предлагать элемент управления, в котором откажут. Окончательное решение всегда за сервером.