Доволі частий кейс, коли на новому проекті, який тільки створює свою інфраструктуру і CI/CD, робиться це як MVP/PoC, і на початку на тюнінг AWS IAM Roles та IAM Policies час не витрачається, а просто підключається AdministratorAccess.
Власне, саме так відбувалось і в моєму проекті, але ми ростемо, і прийшов час навести лад в IAM.
Зміст
Проблема і задача
Отже, маємо GitHub Actions джоби, які деплоять інфрастуктуру з Terraform.
Для доступу до AWS з GitHub використовується Identity Provider з IAM Role: GitHub Actions Worker при старті джоби виконує аутентифікацію та авторизацію в AWS з заданою IAM Role, і потім запускає власне деплой з Terraform.
Для IAM Role зараз підключена політика AdministratorAccess, і наша задача – написати нову fine grained політику, де б не було зайвих доступів.
Варіант перший – це створити пусту політику, підключити її до ролі замість AdministratorAccess, і раз за разом запускати джобу дивлячись на помилки в логах:
А потім по черзі додавати дозволи, наприклад lambda:ListVersionsByFunction
.
Варіант другий – це використати IAM Access Analyzer policy generation:
Він використає CloudTrail events для конкретної ролі та створить IAM Policy в якій будуть тільки ті API-виклики, які дійсно робились цією роллю.
Окрім IAM Access Analyzer є цікава тулза iann0036/iamlive, але в нашому випадку вона не дуже підходить, бо IAM Role використовується в GitHub Actions з AWS Indetity Provider.
Давайте глянемо, як налаштувати IAM Access Analyzer policy generation – створимо CloudTrail, IAM Role, напишемо Terraform-код який буде створювати ресурси, а потім перевіримо які політики нам запропонує Access Analyzer.
Створення CloudTrail Trail
Перше, що нам буде потрібно – це створити CloudTrail Trail, який буде логувати дії. Детальніше про CloudTrail писав в AWS: CloudTrail – обзор и интеграция с CloudWatch и Opsgenie, але зараз нам цікаві тільки типи івентів, які він вміє записувати:
- Management events: все, що стосується змін в ресурсах – створення EC2, VPC, зміни в SecurtyGroups тощо
- Data events: все, що стосується даних – створення об’єктів в S3-бакетах, зміни в таблицях DynamoDB, виклики Lambda-функцій
Отже, якщо наш Terraform-код займається тільки створенням ресурсів в AWS – то має вистачити Management events, якщо ж він додатково виконує якісь дії з даними/об’єктами – то потрібні обидва. Можна включити всі, але майте на увазі, що CloudTrail trails не безкоштовний – див. AWS CloudTrail pricing.
Переходимо в CloudTrail > Trails, створюємо новий Trail:
Включаємо логування обох типів – просто для перевірки, в цьому випадку точно вистачило б тільки Management events:
Для Data events вибираємо які саме сервіси будемо логувати:
Переходимо до IAM.
Створення IAM Role
Додаємо нову роль з Trusted entity type == AWS Account, бо зараз тестувати будемо локально з AWS CLI від свого IAM-юзера, а не через GitHub OIDC Identity Provider:
Підключаємо AdministratorAccess:
Зберігаємо цю роль:
Налаштування AWS CLI
Тестити будемо локально, але імітуємо роботу GitHub Actions.
Що нам треба – це створити AWS CLI Profile, який буде виконувати AssumeRole, яку ми створили, а потім з цим профайлом Terraform буде створювати ресурси в AWS.
В файлі ~/.aws/config
додаємо новий профайл:
[profile iam-test] region = us-east-1 role_arn = arn:aws:iam::492***148:role/iam-generator-test-TO-DEL source_profile = work
source_profile = work
тут – це мій робочий профайл, в якому задані Access та Secrets keys.
Перевіряємо, чи працює IAM Role Assume:
$ aws --profile iam-test s3 ls 2023-02-01 13:29:34 amplify-staging-112927-deployment 2023-02-02 17:40:56 amplify-dev-174045-deployment ...
Окей – корзини бачимо, доступ працює.
Створення Terraform
Напишемо простий код, який буде створювати S3 бакет використовуючи створений вище IAM CLI Profile iam-test
(пам’ятаємо, що ім’я бакету має бути унікальним для заданого AWS Region, інакше AWS спробує створити корзину в іншому регіоні):
provider "aws" { region = "us-east-1" profile = "iam-test" } resource "aws_s3_bucket" "my_bucket" { bucket = "blablabla-bucket-iam-test-to-del" force_destroy = true }
Робимо terraform init
та terraform plan
:
І запускаємо terraform apply
:
... aws_s3_bucket.my_bucket: Creating... aws_s3_bucket.my_bucket: Creation complete after 3s [id=blablabla-bucket-iam-test-to-del] Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Використання IAM Access Analyzer policy generation
Краще зачекати хвилин 5 після запуску Terraform, аби CloudTrail встиг записати всі події, а потім можемо згенерувати IAM Policy для цієї ролі:
Вибираємо період, регіон та створений раніше Trail:
Чекаємо 5-10 хвилин, поки проаналізуються логи CloudTrail (можна перезавантажувати сторінку з F5, бо іноді Status сам не оновлюється):
І дивимось які політики нам пропонуються:
Ціла купа, і основна для нашого тесту – s3:CreateBucket
.
Клікаємо Next, і маємо саму політику в JSON:
Зверніть увагу, що Access Analyzer створив окремі правила на API-виклики, які стосуються всіх бакетів – s3:ListAllMyBuckets
, і окремі правила для викликів, які стосуються конкретного бакету/бакетів – s3:CreateBucket
.
При цьому в Resource
використовується ${BucketName}
, який ми можемо замінити на своє значення:
Зберігаємо та підключаємо цю політику:
І тепер можемо відключити AdministratorAccess.
Але маємо на увазі, що ми виконували тільки створення ресурсів і, відповідно, виконувались API-виклики пов’язані тільки зі створенням корзини.
Тобто, якщо ми зараз приберемо AdministratorAccess і залишимо тільки цю нову політику – то виконати terraform destroy
не зможемо, бо, по-перше – у нас нема права на s3:DeleteBucket
, по-друге – при видаленні корзини AWS має перевірити чи нема в ній об’єктів, а для цього виконується операція s3:ListBucket
– тому отримаємо помилку operation error S3: HeadBucket:
Тож треба виконати всі дії з Terraform, а вже після цього генерувати політику:
І потім відключати AdministratorAccess. Але навіть в такому випадку s3:ListBucket
(для S3: HeadBucket
) треба додавати вручну.
Хоча це вже проблема більш специфічна саме до S3, але може бути подібна і з іншими ресурсами.