# Сервисные учетные записи для AI-агентов без общей ответственности

AI-агентам нужны сервисные учетные записи, но сервисная учетная запись не должна становиться именем, за которым вы прячетесь, когда не знаете, кто разрешил действие. Дайте каждому долгоживущему рабочему процессу собственный идентификатор, закрепите за ним конкретного человека и удалите его, когда процесс завершится. Все, что слабее, создает общую ответственность, то есть после неудачного решения никто не сможет дать полный ответ.

Я видел, как это происходит по знакомому сценарию. Команда создает учетную запись `automation`, выдает ей достаточно прав для нескольких задач, связанных с программированием, и называет настройку временной. Через несколько месяцев ее используют задача развертывания, обновление зависимостей и агент, который меняет инфраструктуру. Учетная запись остается активной, потому что отключение может что-то сломать. Когда она меняет настройку в продакшене, журналы отлично показывают `automation` и почти ничего не объясняют.

Решение не требует грандиозной программы управления идентификацией. Нужны несколько жестких границ: один идентификатор рабочего процесса для одной цели доступа, конкретный человек, который может его одобрить или остановить, свидетельства, связывающие каждое использование с определенным запуском, и путь вывода из эксплуатации, продуманный до выдачи доступа.

## Сервисная учетная запись показывает полномочия, а не исполнителя

Сервисная учетная запись сообщает, какой набор разрешений принял API или система. Она не доказывает, какой агент, промпт, версия кода или человек вызвал запрос. Команды часто смешивают эти две задачи, а потом обнаруживают, что журнал аудита не может объяснить инцидент.

Представим, что `release-publisher` может публиковать артефакты сборки. Успешный запрос, аутентифицированный этой учетной записью, показывает, что были использованы полномочия издателя. Но он не говорит, отправил ли запрос одобренный рабочий процесс релиза, разработчик из локального скрипта или агент, который повторил старую задачу после того, как его владелец ушел домой. Запись аутентификации отвечает на вопрос «какие полномочия?», но не на вопрос «почему именно этот вызов, в этом запуске и сейчас?». 

Разделяйте уровни:

- Идентификатор рабочего процесса хранит узко определенные полномочия.
- Сессия агента указывает на конкретное выполнение процесса.
- Одобрение человека или автоматический триггер объясняет, кто запустил или разрешил выполнение.
- Запись действия фиксирует цель, операцию, результат и время.

Это меняет порядок расследования. Если вызов развертывания выглядит подозрительно, сначала отключите идентификатор рабочего процесса, чтобы остановить дальнейшее использование полномочий. Затем изучите запись сессии: какой процесс воспользовался идентификатором, какую версию кода он запустил и кто или какая система разрешили сессию. Одно имя учетной записи не может вместить всю эту историю, не превратившись в общий контейнер.

RFC 6749 делает полезное, но ограниченное утверждение в описании grant client credentials в OAuth 2.0: клиент может использовать свои учетные данные как разрешение, когда действует от собственного имени. Для ограниченной машинной нагрузки это верно. Но это не означает, что любой процесс, способный предъявить учетные данные, преследует одну и ту же законную цель. Именно когда grant считают полной подотчетностью, возникают проблемы.

У агента также другой профиль риска по сравнению с обычной задачей. Обычная задача обычно идет по фиксированному пути кода. Агент может выбирать команды, формировать запросы, повторять их с измененными аргументами или поддаваться влиянию материалов, прочитанных в репозитории. Сервисной учетной записи все равно нужно стабильное назначение, даже если решения вызывающей стороны меняются. Если вы не можете описать это назначение одним простым предложением, учетная запись, скорее всего, накопила несвязанные задачи.

## Одному рабочему процессу нужен один ограниченный идентификатор

Рабочему процессу нужен собственный идентификатор, если у него отдельные назначение, граница разрешений, владелец, окружение или условие завершения. Не создавайте учетную запись для каждого промпта или короткой задачи. Это добавляет шум, но не усиливает контроль. Создавайте идентификаторы на том уровне, где можно отозвать один набор полномочий, не останавливая несвязанные процессы.

«Обновлять манифесты зависимостей в одобренных репозиториях» и «публиковать подписанные артефакты в продакшен» не должны использовать один идентификатор. Первый процесс меняет исходные файлы и создает запросы на проверку. Второй меняет канал релиза. Даже если оба процесса запускает один агент, разрешения различаются, утверждать их могут разные люди, а реакция на подозрительное действие точно будет разной.

Противоположная ошибка тоже отнимает время: команда разделяет один узкий рабочий процесс на десятки имен учетных записей, потому что каждый запуск агента создает новую. Идентификаторы запусков уже описывают короткие выполнения. Идентификаторы сервисных учетных записей должны описывать устойчивую цель авторизации. Используйте записи сессий для отдельных запусков, а учетные записи оставляйте для полномочий, которые сохраняются между запусками.

Рабочий шаблон именования:

```text
<environment>.<product-or-repository>.<workflow-purpose>

prod.payments.release-publisher
dev.docs.dependency-updater
prod.data.backfill-reader
```

Имена помогают людям, но сами по себе не ограничивают область доступа. Привяжите к каждому идентификатору только те действия, которые нужны его рабочему процессу. Дайте обновляющему зависимости агенту право создать ветку и отправить запрос на проверку, если это все, что он делает. Не выдавайте ему права на релиз только потому, что когда-нибудь они могут понадобиться. Слово «может» создало больше постоянного доступа, чем реальные требования.

Используйте отдельные идентификаторы для разных окружений. Агент в разработке может проводить эксперименты, часто повторять попытки и работать с одноразовыми данными. Такое поведение не должно происходить под продакшен-идентификатором. Копирование продакшен-учетных данных в конфигурацию разработки переворачивает границу: теперь наименее контролируемое окружение владеет самыми сильными полномочиями.

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

## Владельцем должен быть человек, который может остановить работу

У каждого идентификатора рабочего процесса должен быть один конкретный владелец-человек, который обязан отвечать за него и имеет право его остановить. Технический контакт может помогать с эксплуатацией, а команда обеспечивать непрерывность, но ни то ни другое не заменяет конкретного владельца.

Владелец делает четыре вещи. Он подтверждает, что рабочий процесс все еще нужен, одобряет изменения доступа, реагирует на подозрительные вызовы и выводит идентификатор из эксплуатации после завершения работы. Если указанный владелец не может выполнять эти задачи, запись носит декоративный характер.

NIST SP 800-53 Revision 5, контроль AC-2 по управлению учетными записями, требует определить типы учетных записей, установить условия членства в группах и ролях и отключать учетные записи, когда они больше не связаны с пользователем или не нужны. Этот контроль хорошо подходит и для нечеловеческих учетных записей, хотя его часто воспринимают только как рекомендацию для учетных записей сотрудников. У машинного идентификатора без ответственного хранителя нет человека, который установит такие условия или решит, что учетная запись больше не нужна.

Поместите запись об учетной записи в репозиторий или реестр, который использует процесс проверки доступа. Не прячьте ее на странице wiki, расходящейся с реальной привязкой. Для серьезной проверки достаточно таких полей:

```yaml
identity: prod.payments.release-publisher
owner: "Morgan Lee"
technical_contact: "Release engineering on-call"
purpose: "Publish approved signed payment service artifacts to production"
allowed_actions:
  - "upload signed artifact to production release repository"
  - "read release metadata for the payment service"
environments:
  - production
permission_bindings:
  - "artifact-repository/publish-payments-prod"
credential_method: "short lived workload token"
source_repository: "payments-service"
review_after: "2026-06-30"
retire_when: "The payment service stops using this release workflow"
incident_contact: "Release engineering on-call"
```

Такая запись делает неоднозначность заметной. Если `allowed_actions` превращается в абзац с несколькими системами и расплывчатыми глаголами вроде «управлять» или «администрировать», разделите рабочий процесс. Если в `retire_when` указано «никогда» или условие отсутствует, учетная запись попала в стопку постоянного доступа. Если в поле владельца стоит список рассылки, назначьте человека до выдачи доступа.

Изменение владельца требует отдельного контроля. Уходящий сотрудник не должен оставаться номинальным владельцем только потому, что передавать запись неудобно. Новый владелец должен принять учетную запись, прочитать ее назначение и привязки и установить следующую дату проверки. Если никто не принимает ее, отключите учетную запись. Системы не получают исключение из правил владения только потому, что продолжают работать.

## Общие учетные записи превращают небольшой инцидент в угадайку

Общая учетная запись особенно плохо показывает себя во время обычного инцидента, а не только при громком взломе. Представьте, что команда использует `prod.agent-ops` для трех рабочих процессов: агента релиза, агента для сводок по инцидентам и агента для исправления инфраструктуры. Учетная запись может читать состояние развертывания, менять переменную окружения и запускать откат.

В 16:20 кто-то замечает, что переменная окружения изменилась на недопустимое значение. Журнал API говорит, что запрос сделал `prod.agent-ops`. Команда релиза утверждает, что ее запуск завершился раньше. Команда инцидентов говорит, что ее агент читал состояние, но не должен был менять настройки. Владелец инфраструктуры сообщает, что днем тестировался промпт для исправления, но точная сессия не сохранилась. Все три заявления могут быть правдой, а журнал учетной записи не способен разрешить противоречие.

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

Раздельные идентификаторы меняют порядок действий. Если `prod.payments.release-publisher` делает неожиданный вызов, отключите именно его. Учетные записи для сводок и исправлений сохраняют свои полномочия. В записи действия должны быть идентификатор рабочего процесса и ссылка на запуск, чтобы расследующий мог найти точную сессию, не полагаясь на память. Поэтому правило «одна учетная запись на команду» не является компромиссом. Это решение объединить области отказа.

Некоторые команды защищают общие учетные записи тем, что централизованное управление облегчает замену ключей. Это кажется правдой: нужно заменить всего один секрет. Но выигрыш в эксплуатации невелик, а цена проявляется при точечном отзыве, проверке разрешений или объяснении произошедшего. Автоматизируйте выдачу учетных данных и управление привязками, вместо того чтобы расширять границу учетной записи ради удобства.

Не путайте общую учетную запись с общей ролью разрешений. Несколько идентификаторов могут получить одну узко определенную роль, если выполняют одну и ту же разрешенную операцию. Идентификаторы останутся раздельными, поэтому журналирование и отзыв продолжат работать. Повторное использование роли сохраняет управляемость, а повторное использование идентификатора уничтожает атрибуцию.

## Объем разрешений должен следовать действиям, а не амбициям агента

Выдавайте доступ для точного пути действия, который хотите разрешить, а затем требуйте от агента доказать необходимость всего остального. Общие возможности агента не дают ему права на общие полномочия.

Начните с глаголов и объектов рабочего процесса. «Прочитать открытые задачи в репозитории A и создать ветку в репозитории A» описывает действие. «Обслуживать репозиторий A» не описывает. По первой формулировке администратор может подобрать права на чтение и создание ветки. Вторая обычно заканчивается широким доступом на запись, потому что ее нельзя сопоставить с точным разрешением.

Тот же принцип действует для разрешений API. Если процесс читает отчет и публикует комментарий, не выдавайте ему доступ к управлению учетными записями только потому, что API объединил эти конечные точки в привлекательную широкую роль. Создайте меньший набор разрешений, если провайдер это позволяет. Если нет, поставьте перед широкими учетными данными посредник для конкретных действий или пересмотрите, должен ли агент выполнять действие без контроля человека.

Именно здесь многие развертывания агентов допускают тонкую ошибку. Они выдают широкий доступ, потому что агенту нужно изучить контекст перед действием. Чтение контекста и изменение цели требуют разных полномочий. Где возможно, дайте доступ на чтение, а для изменения состояния используйте отдельный идентификатор или путь одобрения. Модель, способная прочитать конфигурацию продакшена, не получает автоматически права ее менять.

До регулярного использования рабочего процесса проверьте границу намеренно неправильными запросами. Для издателя релиза попробуйте опубликовать артефакт другого продукта, удалить релиз и изменить настройки репозитория. Каждый запрос должен быть отклонен на уровне авторизации. Успешная проверка штатного сценария доказывает только, что доступ достаточно широк. Отклоненные соседние действия доказывают, что доступ не слишком широк.

Сохраняйте результат проверки вместе с записью владельца. Достаточно простой таблицы:

| Попытка | Ожидаемый результат | Результат проверки |
| --- | --- | --- |
| Опубликовать одобренный платежный артефакт | Разрешено | Подтверждено |
| Опубликовать артефакт другой службы | Запрещено | Подтверждено |
| Удалить продакшен-релиз | Запрещено | Подтверждено |
| Изменить участников репозитория | Запрещено | Подтверждено |

Не позволяйте агенту выбирать идентификатор из меню мощных учетных записей. Среда запуска должна прикреплять к задаче идентификатор, который ей принадлежит. Агент, способный выбирать между несвязанными полномочиями, часто может обойти спроектированную вами границу.

## Учетные данные должны истекать раньше, чем забытые процессы

Долгоживущие учетные данные усложняют вывод из эксплуатации: забытая копия может продолжать работать после отключения видимой задачи. Предпочитайте краткоживущие учетные данные рабочей нагрузки, выданные проверенной среде выполнения, или поручите контролируемому компоненту выполнить авторизованное действие, передавая агенту только результат.

Это отдельный вопрос от дизайна идентичности. Можно создать прекрасно названную учетную запись с владельцем и все равно потерять контроль, если ее учетные данные лежат в репозитории, локальном файле окружения, расшифровке работы агента или журнале сборки. Запись учетной записи говорит, кто может пользоваться полномочиями. Работа с учетными данными определяет, кто действительно может их предъявить.

Предпочтительная последовательность проста:

1. Проверьте среду вызова или сессию агента.
2. Выдайте учетные данные с коротким сроком действия и ограниченной областью рабочего процесса или выполните запрошенное действие от его имени.
3. Запишите запрошенное действие и решение об авторизации.
4. Завершите сессию и аннулируйте полномочия, относящиеся только к этой сессии.

Не передавайте агенту секрет в открытом виде только потому, что ему нужно вызвать API. Тогда каждый промпт, результат инструмента, трассировка и случайная запись в журнале превращаются в возможный канал распространения учетных данных. Маскирование секретов в выводе консоли помогает после утечки, но не мешает агенту получить и повторно использовать их.

Sallyport использует второй подход для поддерживаемых HTTP- и SSH-каналов: агент запрашивает действие через свое MCP-соединение, а приложение хранит API- и SSH-учетные данные в зашифрованном хранилище и выполняет действие самостоятельно. Это может исключить учетные данные из контекста агента, но не отменяет необходимость отдельных идентификаторов рабочих процессов и владельцев.

Для систем, которым нужно использовать учетные данные напрямую, фиксируйте, где они выдаются, как доставляются, каков их максимальный срок жизни и кто может их отозвать. Замена не должна зависеть от того, вспомнит ли кто-то дату в календаре. Включите выдачу и замену в процесс развертывания рабочего процесса и проверьте замену до того, как учетная запись станет критически важной.

Короткий срок действия учетных данных не позволяет игнорировать журналы. За короткую сессию агент способен причинить серьезный ущерб. Истечение срока ограничивает сохранение доступа после злоупотребления, а область, одобрение и запись действий ограничивают последствия во время сессии.

## Записи аудита должны связывать полномочия с конкретным запуском

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

Используйте единый формат события. Следующий JSON не привязан к конкретному провайдеру, но содержит поля, которых обычно не хватает после инцидента:

```json
{
  "event_id": "act_01J8Q7M6K4",
  "time": "2026-04-14T16:20:31Z",
  "workflow_identity": "prod.payments.release-publisher",
  "run_id": "run_8f3c1d",
  "trigger": {
    "type": "approved_ci_job",
    "initiator": "morgan.lee",
    "source_revision": "a1b2c3d4"
  },
  "authorization": {
    "decision": "approved",
    "approved_by": "morgan.lee",
    "approval_ref": "apr_31fa"
  },
  "action": {
    "target": "production artifact repository",
    "operation": "publish",
    "resource": "payments-service/2.4.1"
  },
  "result": "success"
}
```

Не помещайте в запись аудита учетные данные, полные промпты с чувствительными материалами или неограниченные полезные нагрузки только ради подробного расследования. Фиксируйте достаточно устойчивого контекста, чтобы установить причинную связь, а затем применяйте к нему те же правила работы с данными, что и к любому операционному журналу. Система аудита, превратившаяся во второе хранилище секретов, создает собственный путь к инциденту.

Важна и целостность. Журналы, которые может изменить агент или скомпрометированный рабочий процесс, не разрешат спор. Используйте хранилище только для добавления записей, разделяйте права записи, чтения и администрирования и регулярно проверяйте целостность. Сохраняйте свидетельства после вывода учетной записи из эксплуатации. Вывод удаляет будущие полномочия, но не должен стирать историю прошлых действий.

Журналы Sessions и Activity в Sallyport создаются из доступного только для записи зашифрованного журнала аудита с цепочкой хешей, а команда `sp audit verify` проверяет эту цепочку офлайн без учетных данных хранилища. Для действий, которые Sallyport выполняет через себя, это полезное свидетельство, но окружающим системам все равно нужно хранить владельца рабочего процесса, триггер и контекст бизнес-одобрения.

Стройте проверку вокруг вопросов, на которые можно ответить за несколько минут: у какого процесса были эти полномочия? Кто был его владельцем в тот момент? Какой запуск ими воспользовался? Что одобрило этот запуск? Какая именно операция завершилась успешно или с ошибкой? Если для ответа нужно собирать историю из чатов, записи неполны.

## Вывод из эксплуатации является рабочим процессом, а не ежегодной уборкой

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

Включите условия вывода в исходную запись. Процесс миграции можно завершить после окончания миграции. Автоматизацию репозитория, когда репозиторий архивирован. Интеграцию с поставщиком, когда заканчивается договор. Такие условия упрощают решение, потому что согласованная дата окончания уже записана.

При выводе идентификатора используйте такую последовательность:

1. Запретите новое использование учетной записи и отзовите активные учетные данные или привязки.
2. Наблюдайте за ожидаемыми сбоями в течение установленного периода и найдите неописанные зависимости.
3. Восстановите только минимальный доступ, необходимый для подтвержденной зависимости, назначив нового владельца и создав новую запись, если процесс все еще нужен.
4. Сохраните запись о владельце, историю доступа и журналы действий согласно правилам хранения.
5. После завершения периода наблюдения удалите идентификатор и оставшиеся учетные данные.

Не начинайте с удаления учетной записи. Удаление может стереть полезную конфигурацию и усложнить диагностику. Блокировка сдерживает проблему и позволяет обычному мониторингу показать скрытых вызывающих пользователей. Она также заставляет задать полезный вопрос: если рабочий процесс сломался, кто за него отвечает и почему его не было в реестре?

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

Вывод из эксплуатации нужен и при расширении рабочего процесса. Если обновляющий зависимости агент начинает развертывать код, не изменяйте его назначение так, чтобы оно описывало две несвязанные задачи. Выведите старый идентификатор из эксплуатации или сократите его права, затем создайте идентификатор развертывания с отдельным владельцем, областью, тестами и записью проверки. Так сохраняется смысл аудита обеих учетных записей.

## Подотчетность сохраняется только тогда, когда проверки могут отзывать доступ

Проверка сервисной учетной записи полезна лишь тогда, когда проверяющие видят реальные привязки, знают текущего владельца и могут отключить доступ без недели переговоров. Встройте такие полномочия в операционный процесс до того, как они понадобятся.

Проверяйте рабочие процессы с наибольшим риском чаще, но не превращайте каждую проверку в формальность. Спросите, существует ли еще заявленная цель, сохраняет ли владелец полномочия, соответствуют ли недавние действия назначению и совпадает ли область доступа с наблюдаемым использованием. Если ответ неясен, сократите или отключите учетную запись, пока владелец не внесет ясность.

Оценивайте состояние фактами, которые показывают заброшенные полномочия: учетными записями без владельца, просроченными датами проверки, отсутствием действий за значимый период, учетными данными с истекшим или почти истекшим сроком и рабочими процессами, чьи недавние операции выходят за пределы описанного назначения. Это очередь на проверку, а не показательная статистика.

Первый практический шаг: выгрузите существующие идентификаторы агентов и напишите рядом с каждым одно предложение: «Этот рабочий процесс может делать X для Y, его владелец Z, до наступления условия W». Учетные записи, которые не поддаются такой формулировке, скрывают общую ответственность. Отключите или разделите их до того, как расширять возможности агентов.
