open atlas
↑ К треку
AWS на практике AWS · 07 · 02

Глубже в IAM-evaluation, envelope-шифрование KMS и Secrets Manager

IAM разрешается по порядку: дефолтный deny, явный allow даёт, явный deny всегда побеждает; boundaries и SCP — потолок, conditions сужают statement. Предпочитай роли и STS долгоживущим ключам, останавливай confused deputy через ExternalId, шифруй envelope-ключами KMS.

AWS Senior ◷ 20 min
Уровень
ОсновыJuniorMiddleSenior

Разработчик коммитит .env в публичный репозиторий на девяносто секунд, прежде чем затереть его force-push’ем. Поздно: боты сканируют новые коммиты GitHub за секунды, и долгоживущий IAM-access-key внутри уже собран. Через минуты этот ключ — на пользователе, чья политика несла удобный "Action": "*", "Resource": "*", — поднимает десятки GPU-инстансов под крипто-майнинг во всех регионах и заодно выкачивает S3-бакет. Счёт переваливает за пятизначную сумму ещё до того, как сработают биллинг-алерты. Ключ никогда не ротировался, потому что не был временным; политика никогда не сужалась, потому что wildcard «просто работал»; и ничто не ограничило радиус поражения, потому что не было ни permissions boundary, ни SCP. Не хватало трёх отдельных защит, а взлому хватило бы одной из них на месте. Этот урок — про все три.

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

Как на самом деле принимается решение по запросу

С identity-политиками ты уже познакомился в базовом уроке по IAM: principal, action, resource, effect. Сеньорская модель — это evaluation, то, как AWS сводит несколько типов политик в одно «да/нет» для одного API-вызова. Стартовая точка никогда не нейтральна: каждый запрос по умолчанию — неявный deny. Дальше у правила три хода, и порядок фиксирован.

Первый: явный allow в любой применимой политике может перевернуть дефолт в «разрешено». Второй: типы политик комбинируются — identity-based и resource-based образуют объединение (allow в любой из них достаточно в пределах одного аккаунта), тогда как permissions boundaries и Service Control Policies пересекаются: запрос должен быть разрешён всеми применимыми категориями. Третий, перекрывающий всё: явный Deny где угодно всегда побеждает. Deny в identity-политике, resource-политике, boundary или SCP не перебивается никаким числом allow’ов. Это самое важное предложение во всём IAM, и большинство тикетов «почему у меня нет доступа?» упираются в явный deny, про который кто-то забыл.

Типы политик складываются в слои, у каждого своя роль:

  • Identity-based политики — привязаны к пользователю, группе или роли; дают то, что может делать этот principal.
  • Resource-based политики — привязаны к ресурсу (политика S3-бакета, key policy KMS, политика SQS-очереди); задают, кто может трогать этот ресурс, и уникально включают cross-account-доступ.
  • Permissions boundaries — продвинутая identity-политика, задающая максимум прав, который principal вообще может иметь. Сама по себе ничего не даёт; она ограничивает. Эффективные права principal’а — это пересечение его identity-политик и его boundary.
  • Service Control Policies (SCP) — общеорганизационные ограждения на уровне AWS Organizations. Тоже ничего не дают; они ограничивают, что могут делать все аккаунты и principal’ы под ними, как бы щедро ни выдавал права локальный админ.
  • Session policies — передаются inline при assume роли; они дополнительно сужают права сессии на её время жизни.

Итого: эффективные права = (объединение allow’ов), пересечённое с каждой применимой boundary и каждым применимым SCP, минус любой явный deny. Wildcard-политика из истории давала всё, но без boundary и SCP пересечение никогда не сжималось — потолок был бесконечным, поэтому утёкший ключ унаследовал бесконечный охват. Boundary PowerUserAccess или SCP, запрещающий iam:* вне break-glass-роли и действия вне одобренных регионов, превратили бы катастрофу в локализованное неудобство.

Conditions делают least privilege настоящим

Statement с "Resource": "*" — это риск; statement, суженный Condition, — это least privilege. Condition-ключи проверяют контекст запроса и пропускают statement: aws:SourceIp (только из офисного CIDR), aws:PrincipalOrgID (только principal’ы твоей организации), aws:SecureTransport (только по TLS), aws:RequestedRegion или StringEquals по тегу ресурса (только ресурсы с тегом твоей команды). Statement применяется, только когда его conditions истинны. Эта политика позволяет principal’у читать один бакет, но только по TLS и только из известной сети — убери любое из условий, и запрос будет отклонён:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadReportsTlsAndOffice",
      "Effect": "Allow",
      "Action": ["s3:GetObject"],
      "Resource": "arn:aws:s3:::acme-reports/*",
      "Condition": {
        "Bool": { "aws:SecureTransport": "true" },
        "IpAddress": { "aws:SourceIp": "203.0.113.0/24" }
      }
    }
  ]
}

Более глубокая привычка, которую дают conditions, — роли вместо долгоживущих ключей. Долгоживущий access-key — это статичный, экспортируемый, часто неротируемый credential, ровно то, что утекло в завязке. IAM-роль выдаёт временные credentials через AWS STS (Security Token Service — сервис временных токенов безопасности): короткоживущие, авто-истекающие, никогда не попадающие в репозиторий, потому что существуют лишь минуты. Любая рабочая нагрузка — EC2-инстанс (через instance profile), Lambda, ECS-таск, CI-джоб (через OIDC) — должна ассьюмить роль, а не носить ключ. Когда нужно проаудитить, что вообще выдано, IAM Access Analyzer помечает resource-политики, дающие доступ внешним аккаунтам, и подсвечивает неиспользуемые права, которые можно срезать.

Cross-account-доступ и confused deputy

Cross-account-доступ строится на ролях: роль в аккаунте B имеет trust policy, называющую аккаунт A допущенным к вызову sts:AssumeRole. Principal’ы аккаунта A ассьюмят роль и действуют в B с правами B. Но называние внешнего аккаунта в trust policy открывает классическую дыру — confused deputy. По AWS, это «проблема безопасности, где сущность, не имеющая прав выполнить действие, может вынудить более привилегированную сущность выполнить его». Если ты нанимаешь сторонний SaaS, который ассьюмит роль в аккаунтах многих клиентов, а твой role ARN не секрет, другой их клиент может передать вендору твой ARN и обманом заставить вендора («deputy») действовать на твоих ресурсах.

Фикс — условие ExternalId в trust policy. Вендор генерирует уникальный ID на клиента и передаёт его в каждом вызове AssumeRole; твоя trust policy требует твой ID, поэтому запрос с чужим ID не проходит:

{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Principal": { "AWS": "arn:aws:iam::222222222222:root" },
    "Action": "sts:AssumeRole",
    "Condition": {
      "StringEquals": { "sts:ExternalId": "a1b2c3-unique-per-customer" }
    }
  }
}

Для AWS-сервисных principal’ов в cross-service-сценариях (например, CloudTrail, пишущий в твой бакет) аналогичные ограждения — условия aws:SourceArn и aws:SourceAccount, утверждающие, что сервис действует от имени ресурса или аккаунта, которого ты действительно ждёшь.

KMS: envelope-шифрование и key policies

Зачем шифрование на масштабе требует двух ключей вместо одного? Потому что 5-гигабайтный объект нельзя прогнать через облачный API, а раздать мастер-ключ — значит превратить каждую ротацию в катастрофу. Шифрование на масштабе использует envelope encryption (конверт-шифрование), и KMS построен вокруг него. KMS key (customer-managed key) живёт внутри hardware security module и «спроектирован так, чтобы никогда не экспортироваться из HSM в открытом виде». Байты ключа ты не получаешь никогда. Вместо этого ты вызываешь GenerateDataKey, и KMS возвращает data key в двух формах: открытую копию и копию, зашифрованную под твоим KMS key. Ты шифруешь свой большой payload локально открытым data key, тут же затираешь открытый ключ из памяти и хранишь зашифрованный data key рядом с шифротекстом. Чтобы расшифровать позже, ты отправляешь в KMS только зашифрованный data key, тот разворачивает его и возвращает открытый data key ровно для этой операции. Объёмные данные в KMS не ходят никогда; ходит только маленький data key. Поэтому шифрование S3, EBS и RDS масштабируется — каждый объект получает свой data key, и все они обёрнуты одним центральным KMS key.

Кто может использовать и управлять KMS key, задаёт его key policy — resource-based политика, которая, в отличие от большинства, является первичным контролем доступа к ключу. Опасный footgun: если key policy не даёт доступ root’у твоего аккаунта (или другому административному principal’у), ты можешь навсегда заблокировать всех от ключа — зашифрованные под ним данные становятся невосстановимыми, и даже AWS Support не перекроет политику. Помимо key policy, grants делегируют временное, узко ограниченное использование principal’у (часто AWS-сервису) без правки политики; aliases дают ключу стабильное удобное имя, чтобы ротировать материал, не меняя ссылки; автоматическая ротация (для customer-managed-ключей опциональна, по умолчанию ежегодная и теперь настраиваемая) генерирует новый backing-материал, сохраняя старые версии для расшифровки старых данных; а ключи бывают симметричными (один ключ шифрует и расшифровывает — дефолт для большинства данных) или асимметричными (пара public/private — для подписи или шифрования публичным ключом, который можно раздать). Key policy KMS выглядит так:

{
  "Version": "2012-10-17",
  "Id": "key-policy-app-data",
  "Statement": [
    {
      "Sid": "EnableRootAccountAdmin",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::111122223333:root" },
      "Action": "kms:*",
      "Resource": "*"
    },
    {
      "Sid": "AllowAppToEncryptDecrypt",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::111122223333:role/app-runtime" },
      "Action": ["kms:GenerateDataKey", "kms:Decrypt"],
      "Resource": "*",
      "Condition": {
        "StringEquals": { "kms:ViaService": "s3.us-east-1.amazonaws.com" }
      }
    }
  ]
}

Убери первый statement — и ключ осиротел.

СлойЧто делаетДаёт или ограничивает?Сбой, если отсутствует или неверен
Identity-политикаЧто может этот principalДаётWildcard даёт всё; утёкший ключ это наследует
Permissions boundaryМаксимум, что principal может иметьОграничивает (пересечение)Нет потолка для радиуса поражения
SCPОбщеорганизационное ограждениеОграничивает (пересечение)Нет тормоза на уровне org ни для одного аккаунта
Явный denyПерекрывает любой allowРубильникОпасное действие остаётся разрешённым по умолчанию
Key policy KMSКто использует/управляет ключомДаёт (resource-based)Пропусти root аккаунта — осиротишь ключ навсегда
Почему это работает

Зачем хранить зашифрованный data key рядом с данными, а не просто звать KMS зашифровать payload напрямую? Потому что KMS ограничивает размер данных, которые можно ему передать (несколько КБ), и каждый вызов стоит запроса — ты не прогонишь объект на 5 ГБ через KMS. Envelope-шифрование переворачивает поток: KMS всегда работает лишь с крошечным data key, твой собственный CPU делает массовое симметричное шифрование локально на скорости канала, а обёрнутый data key едет вместе с шифротекстом, так что любой обладатель права decrypt на KMS key восстановит его. Один центральный, аудируемый, ротируемый ключ защищает неограниченный объём данных.

Secrets Manager против Parameter Store

Утёкший .env — это более глубокий урок: секреты не место в коде, образах или env-файлах, запечённых в сборку. AWS Secrets Manager хранит секреты, зашифрованные KMS, и — главная фича — поддерживает автоматическую ротацию. Для credential’ов базы он по расписанию запускает Lambda-функцию ротации: функция создаёт новый credential, обновляет базу и переключает staging-метки (AWSPENDING становится AWSCURRENT, старый — AWSPREVIOUS), так что вызывающие всегда читают живое значение без простоя. Он напрямую интегрируется с RDS, Redshift и DocumentDB.

Более дешёвый и простой собрат — SSM Parameter Store. Его параметры SecureString тоже зашифрованы KMS, у него нет помесячной платы за секрет, и он отлично подходит для конфигов плюс секретов, которые не нужно автоматически ротировать. Трейдофф: у Parameter Store нет встроенной ротации и нет нативной интеграции с базами. Сеньорский выбор: Secrets Manager — для credential’ов, которые должны ротироваться (базы, API-ключи с жизненным циклом); Parameter Store — для статичного конфига и редко меняющихся секретов, где важна цена.

Выбери лучший вариант

Нужно хранить пароль production-базы, который приложение читает на каждом cold start. Комплаенс требует ротировать его каждые 30 дней с нулевым простоем. Выбери хранилище.

Викторина

Identity-политика разрешает s3:DeleteObject на бакете, но SCP на аккаунте содержит явный Deny для s3:DeleteObject. Что произойдёт, когда principal попробует удалить?

Викторина

Ты заводишь роль, которую сторонний SaaS ассьюмит в твоём аккаунте, доверяя его AWS-account-ID. Зачем добавлять условие sts:ExternalId в trust policy?

Вспомните перед уходом
  1. 01
    Сформулируй полное правило evaluation IAM, роль каждого типа политики и как считаются эффективные права.
  2. 02
    Объясни envelope-шифрование KMS от и до и единственную ошибку key policy, осиротяющую ключ.
Итог

Права IAM решаются фиксированным порядком evaluation, а не тем, какая политика выглядит конкретнее. Каждый запрос начинается как неявный deny; явный allow в любой применимой политике может его дать; а явный deny где угодно — в identity-политике, resource-политике, permissions boundary или SCP (Service Control Policy — политика управления сервисами на уровне организации) — всегда побеждает и не перекрывается. Теперь, когда видишь ошибку «Access Denied», а identity-политика явно даёт действие, первый рефлекс — искать явный deny или boundary/SCP, срезающий потолок, а не добавлять ещё один allow. Эффективные права — это объединение allow’ов, пересечённое с каждой применимой boundary и SCP, минус любой явный deny: boundaries и SCP ограничивают потолок, объединение allow’ов заполняет всё под ним, а deny — рубильник. Conditions (aws:SourceIp, aws:PrincipalOrgID, aws:SecureTransport, совпадения по тегам) превращают широкие statement’ы в настоящий least privilege, а роли, выдающие короткоживущие STS-credentials, должны вытеснить долгоживущие access-key’и повсюду — утёкший, неротируемый, wildcard-ключ из истории провалил сразу все три защиты. Cross-account-доступ — это роль, чья trust policy называет другой аккаунт, и confused deputy закрывается через ExternalId (или aws:SourceArn и aws:SourceAccount для сервисных principal’ов). Для данных envelope-шифрование KMS позволяет одному центральному ключу, который никогда не покидает свой HSM, оборачивать per-object data-ключи, так что шифрование масштабируется, ни разу не отправляя массив данных в KMS, — но key policy, пропустившая root аккаунта, осиротит ключ навсегда. Наконец, секреты живут в Secrets Manager, где Lambda-ротация ротирует credential’ы базы без простоя, или в более дешёвом Parameter Store SecureString для статичного, редко меняющегося конфига. Явный deny побеждает, boundaries и SCP ограничивают, conditions сужают, роли бьют ключи, ExternalId останавливает deputy, KMS оборачивает данные, а Secrets Manager держит их свежими.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 5 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

Примени это

Примени этот урок в реальном проекте.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.