GitHub

Tensho

Заметки непутевого программиста

AWS IAM Organization Principal Condition

23/11/2020

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

На днях мне попался любопытный кейс с превышением лимита вложенных в IAM роль политик в рамках мульти-аккаунтной инфраструктуры. Предположим, что у нас есть инфраструктурный компонент мониторинга, который обеспечивает доступ Grafana сервиса к AWS CloudWatch сервису как источнику данных.

AWS IAM Grafana CloudWatch

Этот компонент раскатывает дочернюю IAM роль (агент) в каждом целевом аккаунте с разрешениями доступа к логам и метрикам CloudWatch в нем. В свою очередь каждую такую роль принимает родителськая IAM роль в мониторинг аккаунте.

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

Monitoring Account
################################################################################
# Grafana main IAM role in monitoring account, which assumes agent IAM role in the target account
################################################################################
resource aws_iam_role grafana {
  name               = "grafana"
  assume_role_policy = data.aws_iam_policy_document.ecs_sts_assume_role.json
}

data aws_iam_policy_document ecs_sts_assume_role {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["ecs-tasks.amazonaws.com"]
    }
  }
}
Target Account
################################################################################
# Grafana agent IAM role
################################################################################
resource aws_iam_role grafana_cloudwatch_agent {
  name               = "grafana-cloudwatch-agent"
  assume_role_policy = data.aws_iam_policy_document.grafana_assume_role.json
}

data aws_iam_policy_document grafana_assume_role {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "AWS"
      identifiers = [data.aws_iam_role.grafana.arn]
    }
  }
}

data aws_iam_role grafana {
  provider = aws.monitoring

  name     = "grafana"
}

################################################################################
# Extend Grafana main IAM role with inline policy, which allows to assume agent IAM role
################################################################################
resource aws_iam_role_policy allow_assume_grafana_cloudwatch_agent {
  provider = aws.monitoring

  name     = data.aws_iam_account_alias.current.account_alias
  role     = data.aws_iam_role.grafana.id
  policy   = data.aws_iam_policy_document.allow_assume_grafana_cloudwatch_agent.json
}

data aws_iam_policy_document allow_assume_grafana_cloudwatch_agent {
  provider = aws.monitoring

  statement {
    actions = ["sts:AssumeRole"]

    resources = [aws_iam_role.grafana_cloudwatch_agent.arn]
  }
}

data aws_iam_account_alias current {}

Тут мы расширяем список аккаунтов для родительской роли на стороне компонента. И все хорошо с таким подходом, пока количество аккаунтов не увеличевается до таких размеров, что соответствующее количество вложенных политик превышает стандартный лимит политики в 10240 байта. Для обеспечения дальнейшего масштабирования требуется рефакторинг. И видимо как раз для таких случаев в IAM добавлено весьма полезное условие aws:PrincipalOrgID. Оно позволяет проверить, принадлежит ли доверитель (Principal) к AWS организации. С этой фичей можно не перечислять каждый целевой аккаунт в разрешениях политики, а просто обозначить шаблон доверителя с условием принадлежности аккаунта к организации.

Monitoring Account
################################################################################
# Grafana main IAM role, which assumes agent IAM role in any organization account
################################################################################
resource aws_iam_role_policy allow_assume_grafana_cloudwatch_agent {
  name     = "allow-assume-cloudwatch-agent"
  role     = aws_iam_role.grafana.id
  policy   = data.aws_iam_policy_document.allow_assume_grafana_cloudwatch_agent.json
}

data aws_iam_policy_document allow_assume_grafana_cloudwatch_agent {
  statement {
    actions = ["sts:AssumeRole"]

    resources = ["arn:aws:iam::*:role/grafana-cloudwatch-agent"]

    condition {
      test     = "StringEquals"
      variable = "AWS:PrincipalOrgID"

      values = [data.aws_organizations_organization.organization.id]
    }
  }
}

data aws_organizations_organization organization {
  provider = aws.billing
}
Target Account
################################################################################
# Grafana agent IAM role
################################################################################
resource aws_iam_role grafana_cloudwatch_agent {
  name               = "grafana-cloudwatch-agent"
  assume_role_policy = data.aws_iam_policy_document.grafana_assume_role.json
}

data aws_iam_policy_document grafana_assume_role {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "AWS"
      identifiers = [data.aws_iam_role.grafana.arn]
    }
  }
}

data aws_iam_role grafana {
  provider = aws.monitoring

  name     = "grafana"
}

# We don't need to extend Grafana IAM role with inline policies anymore. 

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

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