IAM (Identity and Access Management) — это система AWS, которая отвечает на один вопрос: «кто и что может делать». Хотите прочитать файл из хранилища, запустить виртуальную машину, отправить сообщение в очередь — каждый раз AWS сверяется с IAM: а разрешено ли вам это вообще.

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

запрос роль, действие, ресурс явный Deny где угодно: отказ явный Allow identity или resource иначе неявный запрет

Порядок решения один для всего AWS: сначала ищут явный запрет, и он перебивает любые разрешения; потом нужен хотя бы один явный Allow; если его нет, действует неявный запрет по умолчанию. Между аккаунтами разрешить должны обе стороны.

Обязательно

Root и первые пять минут в аккаунте

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

Root-пользователь — это учётная запись, на которую регистрировали аккаунт: email и пароль. Она не подчиняется политикам IAM: запретить ей что-либо внутри аккаунта нельзя (только политика уровня организации ставит ей потолок). То есть это не «самый главный администратор», а сущность вне системы прав.

Что с ним делают в первый же день:

  1. Включают второй фактор. Без него один подобранный или утёкший пароль означает потерю аккаунта целиком: злоумышленник сможет сменить контакты, создать себе права и отключить платёжные ограничения.
  2. Удаляют ключи доступа, если они есть. Программе доступ от root не нужен никогда; наличие таких ключей — первое, на что смотрит любой аудит.
  3. Заводят обычного администратора (а лучше сразу вход через IAM Identity Center) и дальше работают под ним.
  4. Записывают, у кого есть доступ к root. Обычно это один-два человека, а пароль и второй фактор лежат в хранилище секретов компании, а не у сотрудника в телефоне.

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

Пользователи против ролей

Это первое и главное различие, в котором новички путаются, и правило тут одно: людям — пользователи, программам, серверам и автоматизации — роли. Почему так — ниже.

IAM-пользователь — это постоянная учётная запись. У неё есть имя, а к нему можно завести долгоживущие ключи доступа (access key и secret key) — что-то вроде логина и пароля, только для программ. Ключей может не быть вовсе (человек просто входит в консоль по паролю), а может быть сразу два — именно так ключ и меняют без простоя: завели второй, перевели на него приложение, старый удалили. Сами по себе ключи не меняются: создали и пользуетесь, пока не отзовёте.

IAM-роль — это не учётная запись, а набор прав, который можно временно «надеть». Аналогия: пользователь — это ваш постоянный паспорт, а роль — гостевой бейдж на проходной, который выдают на пару часов и который сам по себе ничего не значит без вас. Процесс «надеть роль» называется assume role (принять роль), и в ответ вы получаете не вечные ключи, а временные учётные данные (credentials), которые сами протухают через час-другой.

Ключевое правило, которое стоит запомнить сразу: для программ, серверов и автоматизации используют роли, а не пользователей с ключами.

Почему так:

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

На практике это выглядит так: ваше приложение крутится на виртуальной машине (EC2), в контейнере (ECS) или в Kubernetes (EKS). Вы привязываете к нему роль — и приложение получает права через неё. В коде и в конфигах нет ни одного ключа. Когда новичок спрашивает «а как моему сервису достучаться до хранилища S3?» — правильный ответ почти всегда «через роль», а не «положи ключи в переменные окружения».

Как устроены политики

Сами по себе права описываются политикой (policy) — это документ в формате JSON, перечисляющий, что можно, а что нельзя. Вот самая простая политика — «разрешить чтение объектов из конкретного бакета (контейнера хранилища) S3»:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject"],
      "Resource": "arn:aws:s3:::my-bucket/*"
    }
  ]
}

Разберём по полям:

  • Version — версия языка политик. Дата 2012-10-17 не значит, что документ устарел; это просто фиксированный идентификатор актуальной грамматики, его всегда пишут именно так.
  • Effect — Allow (разрешить) или Deny (запретить).
  • Action — что именно: s3:GetObject это «скачать объект». У каждого сервиса свой список действий.
  • Resource — над чем: здесь это ARN (Amazon Resource Name, глобальный адрес ресурса) конкретного бакета. Хвост /* важнее, чем кажется: arn:aws:s3:::my-bucket/* — это объекты внутри бакета, а arn:aws:s3:::my-bucket без хвоста — сам бакет. Действия над объектами (s3:GetObject) просят первый адрес, а получение списка файлов (s3:ListBucket) — второй. Отсюда и классическое «политику написал, файл скачивается, а список не отдаётся».

Политики бывают двух видов, и эта пара важна:

  • Identity-based (политика на личности) — привязана к пользователю или роли. Читается как «этой роли можно читать из этого бакета».
  • Resource-based (политика на ресурсе) — привязана к самому ресурсу. Читается наоборот: «этот бакет разрешает доступ такой-то роли или аккаунту». Такие политики нужны, например, чтобы открыть доступ из другого аккаунта или для сервисов вроде S3 и очередей SQS.

Как AWS принимает решение, можно свести к трём правилам:

  1. По умолчанию запрещено всё (это называется implicit deny, неявный запрет). Если ни одна политика явно не разрешила действие — оно запрещено.
  2. Доступ разрешается, только если есть явный Allow. Внутри одного аккаунта хватает одного Allow — в identity-политике или в resource-политике, неважно в какой. Между аккаунтами разрешить должны обе стороны: identity-политика у того, кто обращается, и resource-политика у ресурса. Исключение, на котором спотыкаются все, — KMS (сервис ключей шифрования): там политика самого ключа обязана разрешить доступ, и одной политики на роли не хватит никогда.
  3. Явный Deny сильнее любого Allow. Если хоть где-то стоит запрет на это действие — всё, доступа нет, и никакие разрешения это не перебьют.

Дополнительно у каждого правила можно задать блок Condition (условие), который ещё сильнее сужает доступ: «только если запрос пришёл из такой-то сети», «только с подтверждением через MFA (второй фактор)», «только с шифрованием». В JSON это выглядит так — правило сработает только при входе с MFA:

"Condition": { "Bool": { "aws:MultiFactorAuthPresent": "true" } }

И есть ещё два механизма, которые режут права сверху — про них полезно знать заранее, потому что именно они объясняют самые загадочные отказы. SCP (service control policy) — правило уровня AWS Organizations: оно ничего не разрешает, а ставит потолок на весь дочерний аккаунт, и то, что не проходит по SCP, внутри аккаунта не включить ничем. Permissions boundary — такой же потолок, только навешенный на конкретную роль или пользователя. Когда Allow вроде есть, а доступа нет, «где-то выше» — это обычно одно из этих двух.

Least privilege — минимум прав

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

Самый частый дефект новичка — написать "Action": "*" и "Resource": "*", то есть «можно всё со всем». Так делают «чтобы не возиться» — и именно это потом превращается в инцидент: одна утёкшая такая роль открывает злоумышленнику весь аккаунт.

На практике least privilege — это итеративный процесс, а не разовая настройка:

  1. Начните узко — дайте минимум, который точно нужен.
  2. Запустите, посмотрите, чего не хватает.
  3. Добавьте недостающее. Повторяйте.

Второй шаг — тот, из-за которого процесс обычно и останавливается, поэтому разберём его по инструментам.

Где видно отказ. Каждый отказанный вызов попадает в журнал вызовов API — CloudTrail. Искать надо записи с полем errorCode равным AccessDenied (иногда UnauthorizedOperation — так отвечает часть вызовов к сети и машинам). В журнале же видно, кто вызывал (userIdentity), что (eventName) и с каким ресурсом, — то есть ровно то, чего не хватает политике.

aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=GetObject \
  --max-results 20 --query 'Events[].CloudTrailEvent' --output text | jq 'select(.errorCode)'

Что читать в самом сообщении об отказе. Современный текст отказа от AWS не абстрактный — он называет и личность, и действие, и, главное, какой вид политики запретил:

User: arn:aws:sts::123456789012:assumed-role/orders-task/abc is not authorized to perform:
secretsmanager:GetSecretValue on resource: arn:aws:secretsmanager:eu-central-1:123456789012:secret:orders/db-AbCdEf
because no identity-based policy allows the secretsmanager:GetSecretValue action

Последняя строка и есть подсказка, где чинить. no identity-based policy allows — не хватает прав у самой роли, добавляйте действие в её политику. with an explicit deny in a resource-based policy — запрет стоит на стороне ресурса (например, в политике бакета), и права роли тут не помогут. with an explicit deny in a service control policy — запрет на уровне организации, и разрешить его внутри аккаунта нельзя вовсе, надо идти к тем, кто управляет организацией. Эти три варианта требуют совершенно разных действий, и различить их можно только прочитав сообщение целиком, а не первое слово AccessDenied.

Чем сузить уже выданное. У IAM есть два штатных инструмента, и оба стоят того, чтобы знать их по имени.

Last Accessed — на странице роли или политики AWS показывает, какими сервисами и действиями роль реально пользовалась за последние месяцы. Всё, чем не пользовались ни разу за девяносто дней, — первый кандидат на удаление из политики.

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

IAM → Roles → orders-task → Generate policy → период и источник CloudTrail

Получившуюся политику не прикладывают слепо: в период могли не попасть редкие операции (ежемесячный отчёт, аварийный путь), и их надо дописать руками. Но как отправная точка вместо "Action": "*" это то, что превращает «сужу права» из недельной задачи в получасовую.

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

STS и временные учётные данные

За всеми временными учётными данными стоит отдельный сервис — STS (Security Token Service, сервис выдачи токенов безопасности). Когда кто-то «принимает роль», под капотом вызывается операция AssumeRole, и STS возвращает короткоживущий набор: access key, secret key и токен сессии. По умолчанию они живут час, максимум можно настроить до 12 часов. Оговорка на будущее: если роль принимают из-под другой роли (так часто выходит при доступе между аккаунтами), потолок жёсткий — час, и никакая настройка его не поднимет.

Команда вручную выглядит так:

aws sts assume-role \
  --role-arn arn:aws:iam::123456789012:role/my-app-role \
  --role-session-name demo-session

Самое полезное для новичка — понять, что на этом механизме построено почти всё:

  • Роль виртуальной машины (instance role) незаметно дёргает STS за вас — SDK сам получает и обновляет учётные данные, вам не надо ничего писать.
  • В Kubernetes (EKS) то же самое делают механизмы IRSA или более новый Pod Identity — они выдают поду временные учётные данные роли.
  • Доступ между аккаунтами — это assume роли в чужом аккаунте.
  • Вход через корпоративную учётку (федерация) — тоже выдача временных учётных данных.

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

Доступ между аккаунтами

В AWS аккаунты — это основная граница изоляции: по умолчанию из одного аккаунта в другой не достучаться никак. Когда такой доступ всё же нужен (например, сервис из аккаунта A должен читать данные в аккаунте B), его делают через роль, а не через общие ключи.

Схема такая:

  1. В аккаунте-владельце ресурса (B) заводят роль.
  2. К роли цепляют trust policy (политику доверия) — она говорит «эту роль разрешено принимать аккаунту A».
  3. К той же роли цепляют identity-политику с нужными правами (например, чтение бакета).
  4. Сервис из аккаунта A вызывает AssumeRole для этой роли, получает временные учётные данные и работает с ними.

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

Где это применяется

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

Где вы столкнётесь с IAM сразу:

Типичные ошибки новичков, которых стоит избегать с первого дня:

  • Класть долгоживущие ключи пользователя в код или переменные окружения вместо использования роли.
  • Выдавать "*" в Action или Resource «чтобы заработало», а потом забыть сузить.
  • Удивляться, что доступа нет, хотя Allow есть — почти всегда виноват явный Deny где-то выше или забытый implicit deny (нигде не разрешили).
  • Хранить ключи в общем доступе для связи между аккаунтами вместо настройки роли с trust policy.
Дополнительно: при первом чтении можно пропустить

Глубже: управляемые политики и встроенныерасширенное

Политику можно приложить двумя способами, и разница видна не сразу, а через полгода.

Управляемая политика (managed) — отдельный объект с именем, который можно приложить к нескольким ролям и пользователям. Правишь в одном месте — меняется у всех. У них есть версии, и можно откатиться на предыдущую.

Встроенная политика (inline) — документ, вписанный прямо в роль или пользователя. Существует только вместе с ним, удаляется вместе с ним и никуда больше не прикладывается.

Что выбирают: почти всегда управляемую. Встроенная оправдана в одном случае — когда права настолько специфичны для этой роли, что отдельный объект только запутает, и важно, чтобы политика исчезла вместе с ролью.

И отдельно про готовые политики AWS. Кроме ваших есть набор готовых, написанных самой AWS (их видно по префиксу arn:aws:iam::aws:policy/). Практика «навесил готовую из списка, потом разберусь» — то, с чего реально начинают, и знать про эти политики надо две вещи.

Первая: самые употребительные из них — очень широкие. AdministratorAccess — это буквально «всё со всем». PowerUserAccess — всё, кроме управления правами. ReadOnlyAccess — чтение всего, включая содержимое хранилищ и значения параметров; это не безобидная политика, как кажется по названию. Есть и осмысленные узкие: AmazonS3ReadOnlyAccess, AmazonRDSDataFullAccess.

Вторая: их нельзя менять. Если готовая политика почти подходит, но нужно чуть сузить, её копируют в свою управляемую политику и правят копию. И AdministratorAccess, выданный «на время, чтобы разобраться», надо снимать в тот же день — эта формулировка и есть самая частая причина того, что через год у половины ролей права администратора.

Глубже: IAM глобаленрасширенное

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

Отсюда две вещи. Роль, созданную «во Франкфурте», не нужно создавать заново «в Ирландии» — она уже есть, потому что региона у неё нет вовсе. И обратное: удаление роли действует сразу везде, поэтому «удалю, тут же ничего нет» — плохая идея, если в другом регионе на неё что-то ссылается.

К слову, журнал вызовов API из предыдущего раздела, наоборот, региональный: вызовы IAM (они обслуживаются в Северной Вирджинии) ищут именно там, а не в своём рабочем регионе. Это вторая половина того же недоумения — «отказ был, а в журнале пусто».

Коротко

  • Root стоит вне IAM: второй фактор, никаких ключей, отдельный администратор и запись о том, у кого есть доступ.
  • Людям пользователи (лучше вход через Identity Center), программам и автоматизации роли: ключи пользователя живут вечно и утекают, учётные данные роли живут час и обновляются сами.
  • Политика это JSON из Effect, Action, Resource и Condition; identity-политика висит на роли, resource-политика на ресурсе, а хвост /* в ARN отделяет объекты от самого бакета.
  • Решение принимается так: по умолчанию запрет, нужен явный Allow (между аккаунтами с обеих сторон), явный Deny сильнее всего, а SCP и permissions boundary режут права сверху.
  • Управляемые политики вместо встроенных; готовые AdministratorAccess и ReadOnlyAccess шире, чем кажутся, их копируют и сужают, а не правят.
  • Минимум прав это процесс: отказ ищут в CloudTrail по AccessDenied, текст отказа называет вид политики, Last Accessed и Access Analyzer показывают, что сузить.
  • Временные учётные данные выдаёт STS через AssumeRole: на нём стоят роль машины, IRSA и Pod Identity, доступ между аккаунтами через trust policy и федерация.
  • IAM глобален, а журнал вызовов IAM живёт в Северной Вирджинии: «отказ был, а в журнале пусто» лечится сменой региона в CloudTrail.

Что почитать дальше