Skip to content

Доступ сервисов и жизненный цикл API-ключей ​

Статус: целевая модель с реализованным ограниченным профилем, 2026-09-24. Работают managed keys и делегирование на точные аккаунты: 24 repository permissions (repository.read, 18 data actions и пять действий удаления, продвижения, хранилища и диагностики: artifact.delete, artifact.promote, storage.read, storage.manage, diagnostics.read), семь отдельных admin actions, ceiling, activation/rotation/revoke, CAS, SDK и проверки перед commit. Широкие system permissions, рекурсивное делегирование, notBefore, импорт старых identities и namespace selectors остаются проектом. Текущие лимиты и отличия задают runbooks. Карта API, план, ADR 0016, принятый профиль ADR 0019.

1. Совместимый прежний профиль ​

Сервисный файл содержит SHA-256 ключа, стабильный id принципала, список repositories, permissions: read|write, необязательный administrator. Один набор permissions применяется ко всем перечисленным репозиториям. Ключи загружаются при старте; API выдачи/отзыва, срока действия и отдельного key ID нет. Изменение файла требует согласованного перезапуска API/readers/worker. Несколько ключей одного id используют общий владелец uploads и сетевой бюджет.

Пользовательские сессии — отдельный механизм: учётные записи и точные grants групп хранятся в PostgreSQL, пересчитываются при каждом запросе. administrator позволяет управлять пользователями/группами, но не даёт автоматического доступа к байтам. write не подразумевает read; часть операций каталога сейчас требует оба права. Источник фактических проверок — application use cases, не название HTTP-метода.

2. Целевая модель ​

СущностьНазначение
ServiceAccountСтабильный принципал отдельного сервиса и окружения: build-agent-prod, release-reader, catalog-indexer. Владеет uploads, jobs, references и общим бюджетом всех своих ключей.
ApiKeyСекрет для аутентификации, keyId, serviceAccountId, ограничения, состояние, срок, автор выдачи. Ротация ключа не меняет владельца данных.
Policy / bindingСписок точных действий над ресурсами. Один binding связывает actions с одним selector; нельзя перемножать actions и ресурсы разных bindings.
Role templateУдобный именованный набор permissions. При назначении раскрывается в версионированную policy; изменение шаблона не расширяет уже выданные права.
AuthorizationContextВнутренний результат проверки: тип принципала, subject ID, credential ID, ревизии policy, эффективные ограничения. Не сериализуется как секрет или публичная модель БД.

Разделяем сервисные аккаунты, пользовательские сессии и будущие краткоживущие допуски передачи. Постоянный сервисный ключ нельзя встраивать в публичный браузер или распространяемый агент всем пользователям. Интеграция получает собственную identity; разделение на окружения задаётся отдельными аккаунтами и репозиториями.

Первая версия не вводит tenant isolation: репозиторные права не являются обещанием многопользовательского SaaS для независимых организаций. Такая изоляция потребует отдельной модели, ключей уникальности и тестов во всех запросах/очередях.

3. Правило принятия решения ​

Для каждой пары (action, canonicalResource) необходимы одновременно:

  1. Активные аккаунт и credential, наступивший notBefore, не истёкший expiresAt, отсутствие отзыва.
  2. Подходящий binding актуальной policy аккаунта.
  3. Подходящий binding ограничений конкретного ключа. Отсутствующий или пустой список запрещает всё, не означает «унаследовать всё».
  4. Выполнение обязательных условий: роль gateway, владелец upload/job/reference, состояние объекта, repository policy и квоты.

Permissions аккаунта и ключа пересекаются по действию и одному ресурсу, а не только по строковым именам actions. Нет подходящего allow — отказ. Wildcard actions, wildcard repositories, deny-исключения и произвольные выражения policy не входят в первую реализацию. Новое разрешение по умолчанию не выдаётся никому. Сервер не доверяет роли, owner или scope из тела запроса.

Разрешения не образуют числовую лестницу «уровень 1–5»: Publisher не старше Auditor. UI показывает понятные роли, сервер проверяет явные permissions. Глобальное управление и доступ к содержимому независимы.

4. Каталог permissions ​

Имена ниже — целевые стабильные идентификаторы. Они должны появляться в contracts и SDK только вместе с работающим use case. Наличие строки в документе не включает функцию сервера.

PermissionРесурс и смысл
repository.readКарточка разрешённого репозитория и его доступные возможности.
artifact.readDescriptor доступного артефакта; не сами байты.
artifact.listОбход артефактов и поиск; выдаёт только разрешённые объекты.
content.readБайты, HEAD и Range конкретного опубликованного объекта.
upload.createРезерв загрузки в репозитории в пределах квоты.
upload.readСостояние/части собственной загрузки.
upload.writeЗапись частей и whole-file PUT собственной загрузки. Whole-file PUT также публикует blob и требует upload.complete.
upload.completeСборка/публикация собственной загрузки, включая async enqueue.
upload.cancelОтмена собственной неопубликованной загрузки.
job.readСостояние собственного задания с проверкой его репозитория.
package.readManifest, идентичность и список доступных UPack.
package.publishРегистрация UPack после проверки исходного blob и manifest.
asset.readСписок путей, текущий pointer и история разрешённых assets.
asset.writeСоздание или CAS-изменение pointer, с проверкой исходного artifact.
asset.restoreНовая ревизия из прежней, с проверкой исходной ревизии и blob.
annotation.read / annotation.writeЧтение / CAS-изменение метаданных, labels и collections. Редактирование не заменяет разрешение на скачивание.
reference.writeСоздание/снятие references своего аккаунта. Чужие ссылки не удаляются.
audit.readАудит разрешённого репозитория; персональные и служебные поля определены отдельно.
artifact.deleteЛогическое удаление, retention и включение очистки. Только managed key, не часть Publisher.
artifact.promoteПродвижение артефакта между репозиториями; прежним принципалам — при read+write.
storage.read / storage.manageЧтение / изменение политик хранения и очистки. Только managed key.
diagnostics.readСобытия хранения и очистки репозитория. Только managed key.
retention.manageБудущая отдельная политика хранения.
event.read / webhook.manageБудущие журнал событий / API подписок в разрешённом репозитории. Вебхуки уже доставляются из файла оператора (ADR 0069) без этого права.
identity.read / identity.manageГлобальное чтение / управление пользователями и группами.
service-account.read / service-account.manageГлобальное чтение / создание и отключение машинных identities. Не даёт права назначать data permissions.
credential.read / credential.manageМетаданные / выпуск, ротация, отзыв ключей конкретного service account. Только в явно заданной области аккаунтов.
policy.read / policy.manageЧтение / изменение bindings. Для расширения действует отдельный предел делегирования.
system.observeДиагностика, будущие метрики и состояние gateways; без secrets и data access.

auth/me, чтение собственной effective policy и OpenAPI доступны любой действующей identity без выдачи data permissions. Публичная liveness остаётся минимальной. Ограничение подробной readiness через system.observe не вводится незаметно для старых health-клиентов: отдельная версия/профиль, описанный в плане миграции.

5. Роли для интеграций ​

ШаблонВыдаваемые права
Downloaderrepository.read, artifact.read, content.read, package.read, asset.read. Для прямой ссылки на известный artifact допустим ещё более узкий ключ с content.read.
CatalogReaderrepository.read, artifact.read, artifact.list, package.read, asset.read, annotation.read; байты не выдаются.
Publisherrepository.read, artifact.read, upload.create/read/write/complete/cancel, job.read, package.publish, asset.write; запись формата upload.create/read/... в этой таблице означает отдельные permissions. Историю/скачивание и аннотации добавлять только при потребности.
MetadataEditorartifact.read, annotation.read/write; не изменяет bytes, версии и pointers.
Auditorrepository.read, audit.read; не публикует и не скачивает.
Observersystem.observe; нет доступа к каталогу и байтам.
IdentityOperatorОтдельно назначенные identity.*, service-account.*, credential.*; policy delegation и data access не добавляются автоматически.

В первой реализации selector — точный repository ID или точный service account ID для credential management; системные actions имеют явный system selector. В одном binding нельзя смешать несовместимые типы ресурса/actions.

Будущий selector группы пакетов/пути вводится только после сквозных тестов из раздела 8. До этого изоляция разных сервисов внутри каталога достигается отдельными репозиториями, а не неподдерживаемыми префиксами в ключе.

6. Жизненный цикл ключа ​

Проектируемый формат: arkvory_<keyId>.<secret>, где secret — 32 случайных байта в base64url без padding. Prefix — только идентификация формата. Храним hash полного credential, сравниваем constant-time, lookup по несекретному key ID; сырой secret доступен только в ответе выдачи. SHA-256 применим к случайному секрету высокой энтропии; пользовательские пароли продолжают использовать scrypt. Hash и внутренние поля проверки не попадают в list/get API.

Состояния: pending → active → revoked; expiresAt делает pending/active непригодным независимо от работы фоновой очистки. Revoked необратим. Отключение аккаунта блокирует все его credentials без их удаления. Audit/tombstone сохраняются по политике эксплуатации.

Проектные пределы первого инкремента: 1000 service accounts, не более 3 активных и 2 pending ключей на аккаунт, 64 bindings и 64 actions на policy, control JSON до 64 KiB. Активный срок по умолчанию 90 дней, максимум 365; pending активация до 15 минут. Это новые предлагаемые лимиты, не настройки текущего runtime; расширять их можно после измерений. Истёкшие/revoked метаданные имеют отдельный лимит/архивирование, чтобы lifetime-история не росла бесконечно.

Выдача и потерянный ответ:

  1. Оператор вызывает POST /api/v1/service-accounts/{id}/keys с Idempotency-Key, названием, явными bindings и сроком; получает 201, key metadata и один secret. Заголовок ответа Cache-Control: no-store.
  2. Новый клиент вызывает POST /api/v1/auth/activate-key с этим pending Bearer. Только этот маршрут принимает pending credential; он повторно проверяет актуальную policy и возвращает 204. Повтор активным ключом — 204 без расширения прав/продления срока.
  3. Повтор выдачи с тем же idempotency key и телом возвращает metadata существующего pending key (200, без secret); другое тело — 409. Сервер не хранит secret для replay. При потере ответа оператор отзывает этот key и создаёт новый с другим idempotency key; pending также истечёт автоматически.

Ротация создаёт новый pending key того же аккаунта с ограничениями не шире старого. Активация нового не отключает старый автоматически: сначала клиент проверяет запросы, затем оператор отзывает старый. План ротации задаёт дедлайн перекрытия не более 24 часов; при потере нового секрета старый не выключается раньше согласованного дедлайна. Ротация, activation и смена policy сериализуются на credential/account revision. Idempotency receipt содержит key ID и fingerprint запроса, не секрет.

Управление чужими ключами не означает право их использовать. credential.manage выпускает ключ только в пересечении effective policy целевого аккаунта и явно делегируемых bindings вызывающего оператора. policy.manage ограничено тем же delegation ceiling; владелец/оператор не может сам изменить этот ceiling. Первый root/bootstrap выдаётся локальной процедурой оператора, вне публичного API. В первой версии machine keys не делегируют permissions по умолчанию, нет цепочек самовоспроизводящихся ключей. Восстановление после утраты последнего администратора — локальная, журналируемая процедура, без публичного backdoor.

7. Отзыв, фоновые операции и квоты ​

Первый managed-key backend проверяет credential/account/policy по authoritative PostgreSQL при каждом новом запросе, без положительного cross-request auth cache. Недоступность authority закрывает новые managed-key запросы. Не читать права с асинхронной реплики с неизвестным lag. Это добавляет зависимость от БД, которую нужно резервировать вместе с содержимым.

После фиксации отзыва новые запросы запрещены. Запрос, прошедший проверку до отзыва, считается начатым; нельзя обещать прерывание уже отправленных байтов. Первый инкремент сохраняет текущую границу начатого download. Мутации повторно проверяют policy/credential в короткой транзакции публикации; отзыв и commit сериализуются на соответствующих ревизиях. Нельзя держать такую блокировку на протяжении 5 GiB передачи.

Job хранит stable subject ID, initiating credential ID, revision и требуемые action/resource; worker заново проверяет их при исполнении и непосредственно перед публикацией. Ревизия enqueue — аудит, не вечное разрешение. При отзыве initiating key задание не публикуется; новый ключ того же аккаунта может явно переавторизовать enqueue после проверки. Сохранённые parts принадлежат аккаунту, ротация не теряет их. Повторный enqueue не создаёт вторую публикацию.

Квоты суммируются на service account, repository и gateway/site; отдельные ключи только сужают лимит. Ротация и создание дополнительных credentials не дают дополнительную полосу. priority выбирается из разрешённого сервером класса, клиент не задаёт произвольный вес. HTTP requests/s, одновременные transfers, bytes/s и резерв staging — разные ограничения. Distributed leases не разрешают превышение общего бюджета при потере координатора; текущие фиксированные shares остаются до отдельной реализации scheduler.

8. Условия для ограничений по папке и группе ​

Это следующий этап, не параметр первого managed-key выпуска:

  • Policy selector типизирован: repository, package namespace, asset path, artifact. Путь нормализуется один раз теми же правилами, что каталог; сегмент tools/ не разрешает tools-private/. Case, Unicode и percent-encoding совпадают с контрактом соответствующего namespace.
  • Прямой artifactId не обходит selector: доступ по bytes/descriptor требует разрешённой привязки к namespace и конкретной immutable версии/revision. Blob hash или deduplication не являются правом доступа.
  • Upload для узкого namespace заранее связывается с publication intent; на complete/register проверяется фактический UPack manifest или конечный asset path. Нельзя загрузить в разрешённую папку и опубликовать в чужую.
  • Copy/pointer/restore проверяют источник и назначение; metadata, search, history, references, счётчики, audit и events фильтруются до лимита/пагинации. Фильтрация в UI или после LIMIT не подходит.
  • Доступ к raw artifacts list/search для namespace-key запрещён, пока запрос не способен доказать все bindings. Никакого fallback к общему repository read.
  • Скачивание по artifact ID, идентичности пакета и пути файла использует один application authorizer; namespace selector проверяется для разрешённого объекта, а не только для формы URL.

9. Пример будущей выдачи ​

Проект, не исполняемый запрос текущей версии. Целевой service account уже имеет право публикации в releases; оператор имеет разрешение делегировать перечисленные actions.

http
POST /api/v1/service-accounts/11111111-1111-4111-8111-111111111111/keys
Authorization: Bearer <operator-token>
Idempotency-Key: issue-ci-release-2026-09
Content-Type: application/json

{
  "name": "ci-release-prod",
  "expiresAt": "2026-12-23T00:00:00Z",
  "bindings": [{
    "resource": {"kind": "repository", "id": "releases"},
    "actions": ["artifact.read", "upload.create", "upload.read", "upload.write", "upload.complete", "upload.cancel", "job.read", "package.publish"]
  }]
}

Этот ключ публикует только в releases; не скачивает чужие байты, не читает аудит, не управляет аккаунтами и не удаляет версии. Publisher не получает эти права из имени роли.

10. Приёмка модели ​

Проверить отказ при отсутствии action, несовпадении repository и при попытке собрать права из двух bindings; межсервисные uploads/jobs/references; истечение/revoke/disable; lost-response issuance; ротацию во время upload; параллельные лимиты ключей; отзыв между enqueue и commit; недоступность БД и stale cache; совпадение запретов для скачивания по ID, пакету и пути файла; отсутствие секретов в logs/audit/OpenAPI; суммарную полосу нескольких ключей одного аккаунта. Для namespace selectors дополнительно матрица alias/artifactId/encoding/search/copy/restore.

Нормативная основа передачи Bearer: RFC 6750; используем Authorization и TLS, не query-string secret. Проверки каждого запрашиваемого объекта соответствуют подходу OWASP API1:2023. Эти источники не задают наши роли, сроки или лимиты — это проектные решения Arkvory.

Managed actions storage.read, storage.manage, diagnostics.read относятся к точному repository binding. Legacy write не включает настройку квот/очистки. Включение и запуск требуют также artifact.delete; scheduled run использует live authorizing key. Контракт.

Arkvory · ProAnimaStudio