# AI-агенты и несколько облачных учетных записей: привязывайте каждое действие

AI-агенты безопасно работают с несколькими облачными учетными записями только тогда, когда каждый запрос указывает одну неизменяемую границу учетной записи, а исполнитель отклоняет любое несоответствие. Если агент может сказать «развернуть в рабочей среде», но не умеет определить, какая учетная запись AWS, подписка Azure или проект Google Cloud стоит за этим словом, ему выдали неоднозначные полномочия.

Команды часто считают это задачей по наведению порядка в IAM. На самом деле это проблема организации выполнения. IAM может правильно выдать доступ к двум учетным записям, но агент, его оболочка или профиль shell все равно выберет не ту. В результате вполне действительные учетные данные внесут вполне действительное изменение не там, где нужно.

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

## Неоднозначные цели дают реальные полномочия

Метка среды не является границей полномочий. «Рабочая среда» объясняет человеку, как команда собирается использовать ресурсы. Но она не сообщает API, в какую учетную запись AWS, подписку Azure, проект Google Cloud, клиентскую организацию, регион или принятую роль нужно отправить запрос.

Это различие кажется придиркой, пока у компании не появляются `prod`, `production`, `prod-old` и `production-sandbox`, разбросанные по разным организациям. Я видел, как псевдонимы учетных записей копировали во время миграций, устаревшие профили shell указывали на выведенные из эксплуатации учетные записи, а безобидная на вид операция чтения попадала в учетную запись, которую специалисты по реагированию на инцидент пытались сохранить. Агенты усугубляют проблему: они воспринимают обозначения на естественном языке буквально и действуют намного быстрее человека, который заметит неоднозначность.

До выполнения цель должна отвечать на все эти вопросы:

- Какой провайдер владеет ресурсом?
- Какая неизменяемая граница учетной записи получает вызов?
- Какую среду организация связывает с этой границей?
- Какая идентичность исполнителя может действовать в этой границе?
- Какие географические или организационные ограничения применяются?

Порядок важен. Сначала идет граница учетной записи, затем метка среды. Если в запросе указано `environment: production`, но нет ID учетной записи или ID подписки, запрос неполон. Отклоните его, а не просите агента вывести недостающую деталь из названия репозитория, заголовка задачи или текущей конфигурации терминала.

Распространенный плохой совет утверждает, что соглашения об именовании учетных записей решают проблему. Они помогают человеку просматривать список, но не могут служить средством контроля. Имена это изменяемые строки. ID учетной записи, ID подписки, ID клиента, номер проекта, UID кластера и ARN роли выдаются провайдером и гораздо надежнее подтверждают идентичность.

## Метка среды не должна выбирать учетную запись

Считайте среду контролируемыми метаданными, прикрепленными к записи цели, а не входными данными, по которым цель определяется во время выполнения. Это не позволит запросу вроде «примени это исправление в рабочей среде» искать актуальную инвентаризацию и выбирать любую учетную запись с подходящим тегом.

Разница имеет практическое значение. В каталоге развертывания может быть много целей с `environment: production`: платежи, внутренние инструменты, региональные сервисы и учетные записи, оставшиеся после приобретения компаний. Каждая остается отдельным назначением. Агент должен выбрать один утвержденный дескриптор, например `aws-prod-payments`, а не отправлять широкий селектор вроде `environment=production`.

Используйте идентификаторы провайдера, которые задают настоящую границу:

| Провайдер | Привязывать к | Не полагаться на |
| --- | --- | --- |
| AWS | ID учетной записи, раздел, ARN роли, разрешенный регион | Псевдоним учетной записи, имя профиля, отображаемое имя роли |
| Azure | ID клиента, ID подписки, область группы ресурсов при необходимости | Отображаемое имя подписки, выбор каталога на портале |
| Google Cloud | Номер проекта и ID проекта, организация или папка при необходимости | Отображаемое имя проекта, активная локальная конфигурация |

ID проекта в Google Cloud обычно достаточно стабилен для запросов, а номер проекта дает дополнительную неизменяемую проверку. В Azure нужны и клиент, и подписка, поскольку одна подписка не описывает контекст идентичности, выпустившей токен. В AWS одного номера учетной записи недостаточно, чтобы понять, какая роль будет использоваться. Привязывайте оба значения.

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

## В реестре целей должны быть факты, а не догадки

Поддерживайте небольшой проверенный реестр исполняемых облачных целей. Это не вторая IAM-система, и он не должен повторять все облачные разрешения. Облачная IAM по-прежнему решает, может ли роль вызвать API. Реестр отвечает на более узкий вопрос: куда может попасть запрос, если он использует этот дескриптор цели?

В примере используются вымышленные идентификаторы, но структура выбрана намеренно:

```yaml
targets:
  aws-prod-payments:
    provider: aws
    environment: production
    account_id: "482901736154"
    partition: aws
    role_arn: "arn:aws:iam::482901736154:role/agent-payments-deploy"
    regions:
      - us-east-1
      - us-west-2

  azure-prod-fulfillment:
    provider: azure
    environment: production
    tenant_id: "2f34c630-1ce8-4ed3-a7ce-8a3286e799a1"
    subscription_id: "841742bf-9db5-4cf8-9f6b-f1778d5b7511"
    scopes:
      - "/subscriptions/841742bf-9db5-4cf8-9f6b-f1778d5b7511/resourceGroups/fulfillment-prod"

  gcp-prod-catalog:
    provider: gcp
    environment: production
    project_id: "catalog-prod-417"
    project_number: "548201736915"
    parent: "organizations/193847561029"
```

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

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

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

## Агент должен запрашивать дескриптор, а не составлять назначение

Агент должен отправлять намерение и дескриптор цели. Он не должен передавать ARN роли, скопированный из команды, произвольное имя облачного профиля или shell-команду с выбранной пользователем подпиской. Такие поля дают агенту контроль над самой чувствительной частью запроса.

Запрос может выглядеть так:

```json
{
  "request_id": "chg-8a31f6",
  "target": "aws-prod-payments",
  "operation": "aws.ec2.reboot_instances",
  "region": "us-east-1",
  "arguments": {
    "InstanceIds": ["i-0abc123def4567890"]
  },
  "reason": "Recover the failed checkout worker after approved release"
}
```

Исполнитель разрешает `aws-prod-payments` по записи, которая уже хранится у него. Он принимает только указанную роль, отклоняет `eu-west-1`, поскольку этого региона нет в записи, и отправляет запрос с идентичностью выбранной учетной записи. В рамках этого обмена агент никогда не получает многоразовые облачные учетные данные.

Предотвращаемый сценарий легко представить. Агент запускает команду с `--profile prod`, но локальный профиль разработчика `prod` указывает на общую сервисную учетную запись. Команда синтаксически правильна, API принимает ее, а команда замечает ошибку только тогда, когда ожидаемый платежный процесс все еще работает. Дескриптор цели, разрешаемый вне агента, и последующая проверка идентичности остановят команду до того, как запрос на перезапуск покинет исполнителя.

Не принимайте одновременно дескриптор и назначение, переданное вызывающей стороной, «для гибкости». Так появляются два источника истины. Если запрос содержит `target: aws-prod-payments` и другой ARN роли, исполнитель должен отклонить запрос. Он не должен выбирать поле, которое выглядит более конкретным.

## Проверяйте активную вызывающую сторону перед записью

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

Для AWS AWS описывает `GetCallerIdentity` как операцию, возвращающую учетную запись, ARN и ID пользователя вызывающей идентичности. В документации также указано, что вызов возвращает эти сведения даже в ситуации, когда явный запрет обычно заблокировал бы его. Поэтому это хороший инструмент диагностики и предварительной проверки. Он не дает права на другие действия и не заменяет сравнение с ожидаемой записью.

Предварительная команда имеет примерно такой формат:

```bash
aws sts get-caller-identity --output json
```

```json
{
  "UserId": "AROAEXAMPLEID:agent-run-8a31f6",
  "Account": "482901736154",
  "Arn": "arn:aws:sts::482901736154:assumed-role/agent-payments-deploy/agent-run-8a31f6"
}
```

Сравните `Account` с `account_id` цели. Разберите ARN и сравните принятую роль с настроенной ролью. Не используйте проверку по подстроке вроде «в ARN есть payments». До записи отклоняйте другой раздел, учетную запись или роль.

Применяйте тот же подход в других системах. В Azure запросите активные клиент и подписку, затем сравните оба значения с выбранной целью. В Google Cloud запросите активный проект и аутентифицированного субъекта, а перед изменяющим API-вызовом проверьте проект по записи цели. Эти проверки должны выполняться в том же контексте процесса, который отправляет действие. Проверка в одном терминале и выполнение в другом дает лишь ощущение безопасности.

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

## Широкие роли для разных учетных записей скрывают ошибку до серьезных последствий

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

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

Используйте встроенные в облако условия доверия, чтобы сузить способы принятия роли. Доверительные политики ролей AWS могут ограничивать доверенного субъекта и требовать внешний ID, если такая схема подходит вызывающей стороне. Идентичности рабочих нагрузок Azure можно ограничить утверждениями федеративных учетных данных и назначениями ролей ресурсов. Имперсонацию сервисной учетной записи Google Cloud можно ограничить субъектами, которым разрешено выпускать токен доступа. Механизмы различаются, но принцип один: одна идентичность исполнителя должна соответствовать одной проверенной цели и назначению.

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

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

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

Для перезапуска экземпляра AWS полезное подтверждение выглядит так:

```text
Target: aws-prod-payments
Environment: production
Account: 482901736154
Identity: agent-payments-deploy
Action: ec2:RebootInstances
Region: us-east-1
Resource: i-0abc123def4567890
Reason: Recover failed checkout worker after approved release
```

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

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

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

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

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

Для каждого действия записывайте до и после выполнения такие поля:

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

AWS CloudTrail, Azure Activity Log и Google Cloud Audit Logs фиксируют активность на стороне провайдера с учетом используемых сервисов и настроек журналирования. Храните эти записи в отдельной границе безопасности. Не считайте журнал активности агента заменой свидетельствам провайдера: скомпрометированный локальный процесс может солгать о запросе, который он никогда не завершал.

Sallyport записывает запуски агентов и отдельные вызовы в разные журналы, формируемые из зашифрованного аудита с цепочкой хешей. Команда `sp audit verify` может проверять эту цепочку офлайн поверх шифротекста, что помогает установить, изменялись ли локальные записи действий после инцидента.

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

## Инцидент с неверной учетной записью обычно начинается до API-вызова

Видимая проблема часто выглядит как изменение рабочей среды не в той учетной записи. Более ранняя ошибка обычно связана с неконтролируемым способом разрешения цели. Рассматривайте инцидент как дефект привязки, а не просто как случай, когда кто-то «использовал неправильный профиль».

Представим правдоподобную последовательность. В репозитории есть скрипт, принимающий `ENV=prod`. Его оболочка преобразует это значение в профиль AWS с именем `prod`. Во время миграции разработчик изменил этот профиль так, чтобы он указывал на общую сервисную учетную запись, поскольку старой платежной учетной записи больше не требовался прямой доступ. Позже агент получает запрос перезапустить платежный процесс. Он находит скрипт, устанавливает `ENV=prod` и запускает его. Разрешение профиля выбирает общую сервисную учетную запись. У роли широкие разрешения EC2, поскольку команда использовала ее для удобства эксплуатации. Перезапуск успешно выполняется не в той учетной записи.

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

Исправляйте этот класс ошибок в определенном порядке:

1. Отключите затронутую сессию или путь к роли и сохраните записи аудита агента и облака.
2. Определите точный селектор, выбравший не то назначение: имя профиля, подписку по умолчанию, тег среды или ARN роли, переданный вызывающей стороной.
3. Замените этот селектор утвержденным дескриптором цели, который разрешает исполнитель.
4. Добавьте сравнение активной идентичности, отклоняющее любое несоответствие до записи.
5. Разделите широкую роль, если она позволяла действия в несвязанных учетных записях.

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

## Внедряйте защиту сначала для самых рискованных операций записи

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

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

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

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