Читать 6 мин

Разрешения инфраструктуры для AI-агентов и безопасный apply

Разрешения AI-агентов для инфраструктуры должны связывать проверенный план, подтверждённый целевой аккаунт, срок действия и протестированный откат до запуска apply.

Разрешения инфраструктуры для AI-агентов и безопасный apply

AI-агентам не следует выдавать постоянное разрешение, которое означает «применять изменения инфраструктуры». Им нужна узкая авторизация с ограниченным сроком действия: применить одно проверенное изменение к одному подтверждённому целевому аккаунту, имея процедуру восстановления, которую оператор прочитал и принял.

Я видел, как команды принимали Terraform-план за средство безопасности, а кнопку согласования за решение. По отдельности ни то ни другое недостаточно. План можно заново создать для другого аккаунта. Согласование может пережить изменившийся коммит. В заметке об откате может стоять слово «вернуть», хотя изменение уничтожает данные или меняет зависимость, которой уже не существует.

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

Разрешение должно быть связано с действием, а не с должностью

Разрешения AI-агента на работу с инфраструктурой должны санкционировать конкретное изменение, а не категорию вроде «развёртывание», «Terraform» или «операции в production». Широкие категории кажутся удобными, потому что люди мыслят ролями. Но облачные API выполняют запросы, а у запросов есть цели, параметры, идентификаторы и последствия.

Полезное разрешение на apply как минимум связывает следующие факты:

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

Важно различать разрешение рассчитать изменение и разрешение выполнить его. Разрешите агенту изучать репозитории, обращаться к API только для чтения, запускать проверки и предлагать план. Не считайте молча эти действия доказательством того, что агент может менять production. Доступ для изучения обычно раскрывает рабочие сведения. Доступ на изменение влияет на счёт, доступность и иногда на объём данных, которые останутся после инцидента.

Роль с названием InfrastructureDeployer не является объектом согласования. Это деталь реализации. Если она позволяет применять любые изменения в любом аккаунте всякий раз, когда процесс её получает, то ваша процедура согласования зависит от того, вспомнят ли люди о правильном использовании роли. Агенты работают достаточно быстро, чтобы превратить такую проверку памяти в инцидент.

Проверенный план должен быть тем же артефактом, который применяется

Проверенный план имеет смысл только тогда, когда команда apply использует именно его. Повторный запуск terraform plan после согласования создаёт новый артефакт, даже если файлы выглядят неизменными.

Документация команд Terraform ясно показывает это различие. terraform plan -out=FILE записывает файл плана, предназначенный для terraform apply FILE; terraform apply без сохранённого плана сначала создаёт новый план и только потом запрашивает подтверждение. Второй вариант подходит для интерактивной сессии человека. Для рабочего процесса агента, который заявляет о проверке изменений человеком, он неверен.

Используйте сохранённый план и преобразуйте его в JSON для проверки. Минимальная последовательность команд в shell выглядит так:

set -euo pipefail

git rev-parse HEAD \u003e evidence/source-revision.txt
terraform init -lockfile=readonly
terraform plan -out=evidence/change.tfplan
terraform show -json evidence/change.tfplan \u003e evidence/change.json
shasum -a 256 evidence/change.tfplan \u003e evidence/change.tfplan.sha256
terraform providers lock -platform=darwin_arm64

Дайджест имеет простой вид:

f1f5f2...a94c  evidence/change.tfplan

В записи согласования нужно указать полный дайджест, а не просто прикрепить снимок экрана терминала. Затем runner apply проверяет его перед выполнением:

shasum -a 256 -c evidence/change.tfplan.sha256
terraform apply -input=false evidence/change.tfplan

Это предотвращает распространённую ошибку: агент открывает pull request, создаёт план, получает комментарий с согласованием, затем забирает последнюю ветку main и запускает новый план. Между этими действиями коллега мог объединить другое изменение. Поздний план может добавить удаление, сменить версию образа или указать на другой alias провайдера. Проверяющий согласовал вчерашнюю графовую структуру изменений, а не то, что runner видит сейчас.

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

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

Именованная цель должна исходить из учётных данных, а не из метки

Имя цели prod почти ничего не доказывает. Репозитории копируют названия каталогов. Workspace расходятся. Переменные окружения остаются в shell. Агент может точно следовать конфигурации с названием production, будучи авторизованным в sandbox, или, что ещё хуже, в production-аккаунте, выбранном унаследованным профилем.

Пусть исполняющая сущность запросит у облачной управляющей плоскости сведения о себе перед планированием и ещё раз непосредственно перед apply. Сохраните результат в пакете доказательств и сравните его с одобренной целью.

Для действия на базе AWS проверка может быть такой простой:

aws sts get-caller-identity --output json \u003e evidence/caller-identity.json
cat evidence/caller-identity.json

Результат содержит аккаунт и principal:

{
  \"UserId\": \"AROAXXXXX:apply-run\",
  \"Account\": \"123456789012\",
  \"Arn\": \"arn:aws:sts::123456789012:assumed-role/InfraApply/apply-run\"
}

Для Google Cloud отдельно запишите активный аккаунт и проект. Для Azure укажите ID подписки и tenant ID из контекста учётных данных. Не полагайтесь на отображаемое имя облачного аккаунта, поскольку люди могут создавать одинаковые имена. Числовые или глобально уникальные идентификаторы читать менее удобно, зато выполнять такую проверку безопаснее.

Проверка перед apply должна использовать тот же путь к учётным данным, что и сам apply. Это кажется очевидным, но обёртки часто нарушают правило. Скрипт планирования может использовать краткосрочную роль, а последующая команда унаследует профиль разработчика по умолчанию. Агент может вызвать удалённый runner: планировать локально, а применять удалённо под сервисной учётной записью, которую он никогда не проверял.

Добавьте в runner утверждение о цели, которое можно проверить автоматически. Этот пример откажется работать с неожиданным AWS-аккаунтом:

expected_account=\"123456789012\"
actual_account=\"$(aws sts get-caller-identity --query Account --output text)\"

if [ \"$actual_account\" != \"$expected_account\" ]; then
  printf 'refusing apply: expected account %s, got %s\\n' \\\n    \"$expected_account\" \"$actual_account\" \u003e\u00262
  exit 1
fi

Эта проверка не заменяет участие человека. Она ловит целый класс ошибок до того, как какой-либо API изменит инфраструктуру. Дополните её конфигурацией провайдера, где указаны разрешённый аккаунт или подписка, если провайдер поддерживает такую защиту. Проверка на уровне провайдера и проверка на уровне runner завершаются независимо друг от друга, именно этого и нужно добиваться.

Расхождение состояния делает старые согласования небезопасными

План описывает желаемое изменение относительно определённого наблюдаемого состояния. Он не обещает, что это состояние сохранится через час.

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

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

Нужно также правило аннулирования. Отклоняйте план, если после проверки произошло одно из следующих изменений:

  • Изменилась исходная ревизия, набор переменных, ссылка на модуль или файл блокировки провайдеров.
  • Workspace, backend, аккаунт, подписка, tenant, проект или регион отличаются от одобренных данных.
  • Дайджест плана не совпадает с записью согласования.
  • Блокировка состояния, обновление или предварительное условие сообщают о конфликте, меняющем предлагаемое действие.

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

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

Откат должен описывать состояние, которое можно восстановить

Храните секреты для apply вне агентов
Sallyport подставляет HTTP- или SSH-учётные данные из зашифрованного хранилища и никогда не передаёт их агенту.

Понятный путь отката объясняет, как вернуть сервис в приемлемое состояние, кто может это сделать, какие данные окажутся под угрозой и когда нужно остановиться. «Запустите Terraform destroy» и «отмените коммит» редко соответствуют этому стандарту.

Инфраструктурные изменения относятся к разным классам восстановления. Если обращаться с ними одинаково, появляется ложная уверенность.

Обратимое изменение конфигурации, например правило security group или вес load balancer, может поддерживать прямое обратное применение из известной рабочей ревизии. Для замены группы инстансов перед откатом могут потребоваться проверки ёмкости. Миграция базы данных после записи данных может стать необратимой, поэтому восстановление может потребовать миграции вперёд, восстановления из проверенной резервной копии или feature flag, который остановит новые записи.

Описывайте путь отката рабочим языком. Хорошая запись отвечает на следующие вопросы:

  1. Какое наблюдаемое условие говорит о необходимости отката, например неудачные проверки состояния, рост числа ошибок или провал smoke-теста?
  2. Какая именно ревизия, набор параметров или команда возвращает сервис к известной рабочей конфигурации?
  3. Какое предварительное условие должно существовать, например точка восстановления резервной копии, свободная ёмкость или одобренное окно обслуживания?
  4. Кто имеет право действовать, если сессия агента завершилась или исходный согласующий недоступен?
  5. Что нельзя восстановить автоматически, включая записи, секреты, публичные адреса или ручные изменения в облаке?

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

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

Runner apply должен отказываться от неоднозначности

Runner, который выполняет изменение, должен самостоятельно проверять все факты согласования. Бот, способный получить сообщение в чате «можно», не умеет надёжно отличать подтверждённый план от случайной команды.

Используйте структурированную запись согласования. Она может храниться в подписанной системе развёртывания, защищённой записи репозитория или другом контролируемом хранилище. Важнее не выбор места, а поля и процедура проверки. Этот пример JSON показывает структуру:

{
  \"change_id\": \"infra-2025-041\",
  \"source_revision\": \"4ad7d2f\",
  \"plan_sha256\": \"f1f5f2...a94c\",
  \"target\": {
    \"cloud\": \"aws\",
    \"account_id\": \"123456789012\",
    \"region\": \"us-east-1\",
    \"workspace\": \"production\"
  },
  \"approved_by\": \"operator-id\",
  \"expires_at\": \"2025-04-18T15:30:00Z\",
  \"rollback_ref\": \"runbook: payments-api capacity revert\"
}

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

Погашать согласование важно. Без этого агент сможет повторить одобренное действие позже, когда среда уже изменится. Неудачный apply тоже требует явного статуса. Не помечайте его завершённым только потому, что runner отправил команду. Запишите, вернул ли Terraform успех, остаётся ли операция облачного API незавершённой и принял ли оператор получившееся состояние.

Ограничьте поверхность записи агента. Ему могут понадобиться HTTP-вызовы для API развёртывания или SSH-доступ к контролируемому runner, но не передавайте ему многоразовый облачный секрет в контексте. Sallyport хранит API- и SSH-учётные данные в зашифрованном хранилище, выполняет запрошенное действие и возвращает результат агенту. Это помогает предотвратить копирование учётных данных, но не делает расплывчатый запрос на apply безопасным.

Согласование каждого вызова должно быть на опасной границе

Не передавайте учётные данные runner
Агенты получают результаты, а Sallyport сам выполняет HTTP API-вызов или SSH-команду.

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

Размещайте участие человека там, где оно принимает содержательное решение: согласовать план, связанный с целью, а затем разрешить ограниченный запуск apply. Отдельные подтверждения оставьте для действий с необычно большим радиусом поражения, например ротации секретов, удаления защищённого объекта, аварийного доступа или команды за пределами обычного контракта runner.

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

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

Неудачный apply требует другого решения, чем успешный

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

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

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

Используйте такую последовательность решений:

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

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

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

Сделайте первый production-релиз намеренно скучным

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

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

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

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

Вопросы и ответы

Можно ли разрешить AI-агенту запускать Terraform apply?

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

Что должно входить в согласование инфраструктурного apply?

Надёжный практический вариант связывает согласование с конкретным дайджестом плана, идентификатором целевого аккаунта, workspace или подпиской и сроком действия. Процесс apply должен отклонять артефакт, который отличается от одобренного. Широкое согласование вроде «развёртывание в production» оставляет слишком много места для расхождений.

Достаточно ли файла Terraform-плана для согласования развёртывания?

Сохранённый план Terraform полезен только тогда, когда можно доказать, какая конфигурация, какие провайдеры, входные данные состояния и переменные его создали. Сохраните бинарный план или проверенное JSON-представление и свяжите согласование с его криптографическим дайджестом. Повторное создание плана после согласования лишает эту процедуру смысла, потому что изменения могли стать другими.

Как доказать, какой облачный аккаунт изменит агент?

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

Нужен ли автоматический откат для каждого инфраструктурного изменения?

Нет. Для каждого развёртывания нужен способ восстановления, но он зависит от типа изменения. Для миграции схемы базы данных может потребоваться проверенное исправление вперёд или процедура восстановления, а правило security group может позволять прямой откат. Не делайте вид, будто у каждого изменения есть автоматическая кнопка отмены.

Стоит ли использовать разные облачные учётные данные для планирования и apply?

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

Когда агенту следует заново создавать инфраструктурный план?

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

Почему постоянные облачные права администратора опасны для агентов?

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

Какие записи аудита нужно хранить для AI-развёртывания инфраструктуры?

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

Может ли Sallyport контролировать действия AI-агентов в инфраструктуре?

Sallyport может не допустить передачу API- и SSH-учётных данных MCP-совместимому агенту и потребовать авторизацию на сессию или подтверждение для каждого вызова перед выполнением действия. При этом в процессе развёртывания всё равно нужно самостоятельно спроектировать привязку к проверенному плану, проверку целевого аккаунта и данные для отката. Ни один шлюз учётных данных не сможет правильно вывести эти условия из расплывчатого запроса.

Sallyport

Sallyport выполняет API-вызовы и SSH-команды за вашего ИИ-агента. Ключи остаются в локальном хранилище на вашем Mac; вы подтверждаете каждый запуск, и каждое действие попадает в запечатанный журнал.

© 2026 Sallyport · Открытый код по лицензии Apache-2.0 · Oleg Sotnikov