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

Привычка выдавать полный доступ всем подряд и её реальная цена

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

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

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

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

Анатомия точной политики с действиями, ресурсами и условиями

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

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadExportFiles",
      "Effect": "Allow",
      "Action": ["s3:GetObject"],
      "Resource": "arn:aws:s3:::acme-exports/2025/*"
    },
    {
      "Sid": "ListOnlyExportPrefix",
      "Effect": "Allow",
      "Action": ["s3:ListBucket"],
      "Resource": "arn:aws:s3:::acme-exports",
      "Condition": {"StringLike": {"s3:prefix": "2025/*"}}
    }
  ]
}

Здесь три уровня сужения. Действие ограничено одним s3:GetObject вместо s3:*: бот не может ни писать, ни удалять, ни менять права доступа. Ресурс отсекает соседние бакеты и даже чужие префиксы внутри своего. Условие на префикс нужно потому, что s3:ListBucket привязывается к самому бакету, а не к объектам, и без него бот видел бы имена файлов из соседних папок. Каждая строчка читается как ответ на вопрос "что именно делает этот сервис", а не как договорённость на будущее.

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

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

Когда под один бакет работают несколько инженеров, выручает подстановка имени субъекта прямо в шаблон ресурса: префикс вида home/${aws:username}/ отсекает чужие файлы без десятков отдельных политик.

Рабочий цикл сужения прав от широкого доступа к точному

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

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

Спорные операции проверяются симуляцией, которая отвечает allowed или denied для каждого сочетания действия и ресурса, ничего по-настоящему не выполняя:

aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::123456789012:role/export-bot \
  --action-names s3:PutObject \
  --resource-arns arn:aws:s3:::acme-exports/2025/march.csv

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

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

Уточнение прав по журналу CloudTrail и последнему доступу

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

Второй инструмент отвечает на вопрос "а этим вообще кто-нибудь пользуется". Сервис последнего доступа показывает, когда и в каком регионе к политике обращались в последний раз:

aws iam generate-service-last-accessed-details \
  --arn arn:aws:iam::aws:policy/AdministratorAccess
aws iam get-service-last-accessed-details --job-id JOBID123456

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

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

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

Условия, явный запрет и границы полномочий в связке

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

{
  "Sid": "DenyInsecureTransport",
  "Effect": "Deny",
  "Action": "s3:*",
  "Resource": "*",
  "Condition": {"Bool": {"aws:SecureTransport": "false"}}
}

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

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

Отдельного упоминания заслуживают условия по меткам. Если каждому ресурсу присвоена метка команды, право на управление легко ограничить одним условием StringEquals по aws:ResourceTag, и новая команда больше не требует новой политики. Такой приём масштабирует модель точных прав без роста числа документов: правила остаются прежними, меняется только значение метки.

Регулярный аудит прав и дисциплина удаления лишнего

Разовая чистка бесполезна: через полгода всё возвращается, если нет привычки пересматривать. Минимальный набор дисциплины начинается с выгрузки всех ролей и политик одной командой:

aws iam get-account-authorization-details > account-audit.json

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

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

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

Где широкие права оправданы и каким бывает разумный компромисс

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

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

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