Создание роли, защищенной MFA, с помощью AWS CDK обходит условие MFA

Я пытаюсь создать защищенную MFA роль, предоставляющую AdministratorAccess, которую может взять на себя другой пользователь. Я могу заставить его определить пользователя и политику разрешений роли. Однако, когда я определяю требование MFA, основное и разрешенное действие дублируются в политике разрешений, что делает условие требования MFA бесполезным.

Мой код CDK выглядит так:

from aws_cdk import (
    aws_iam as iam,
    core,
)


class AssumeRoleStack(core.Stack):

    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        user = iam.User(self, 'myuser')
        role = iam.Role(self, 'myrole',
                        assumed_by=iam.ArnPrincipal(user.user_arn),
                        max_session_duration=core.Duration.hours(8))
        role.add_managed_policy(iam.ManagedPolicy.from_aws_managed_policy_name('AdministratorAccess'))
        role.assume_role_policy.add_statements(
            iam.PolicyStatement(principals=[user],
                                actions=['sts:AssumeRole'],
                                conditions={'Bool': {'aws:MultiFactorAuthPresent': True}})
        )
        user.add_to_policy(iam.PolicyStatement(actions=['sts:AssumeRole'], resources=[role.role_arn]))

Результирующая политика разрешений роли выглядит следующим образом:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::012345678910:user/assume-role-myuserZ09A543B-1ULCILBM447SF"
      },
      "Action": "sts:AssumeRole"
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::012345678910:user/assume-role-myuserZ09A543B-1ULCILBM447SF"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "Bool": {
          "aws:MultiFactorAuthPresent": "true"
        }
      }
    }
  ]
}

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

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::012345678910:user/assume-role-myuserZ09A543B-1ULCILBM447SF"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "Bool": {
          "aws:MultiFactorAuthPresent": "true"
        }
      }
    }
  ]
}

Я уже пытался определить роль без аргумента assumed_by и вместо этого добавить принципала в assume_role_policy, но для класса iam.Role требуется принципал. Точно так же iam.PolicyStatement в assume_role_policy.add_statements() не позволяет исключить ни аргумент principals, ни аргумент actions.

Как я могу создать политику разрешений роли без избыточности и без того, чтобы из-за них условия бесполезны?


person Milo    schedule 13.01.2020    source источник


Ответы (3)


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

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

iam.Role(
   assumed_by=iam.AccountRootPrincipal()
)
person Vikyol    schedule 13.01.2020
comment
Я не совсем понимаю, как это мешает пользователю взять на себя роль без MFA? С моей точки зрения, это кажется неявным предположением о том, что у пользователя root установлен MFA. Исходя из опыта консалтинга, могу сказать, что это не всегда так (к сожалению). - person Milo; 14.01.2020
comment
Как уже говорилось, это обходной путь, который по умолчанию позволяет только пользователю root взять на себя эту роль. Таким образом, myuser не может взять на себя роль myrole без MFA, когда вы добавляете свой PolicyStatement. - person Vikyol; 15.01.2020

Я понял это сейчас. Решение представляет собой комбинацию решения, предложенного @Vikyol, и подсказок добрых людей по адресу эта проблема с Github:

Вы должны создать класс, который наследуется от iam.AccountRootPrincipal и переопределяет свойство policy_fragment, чтобы возвращаемая политика доверия содержала блок Conditions:

from aws_cdk import (
    aws_iam as iam,
    core,
)


class MFAAccountRootPrincipal(iam.AccountRootPrincipal):
    def __init__(self):
        super().__init__()

    @property
    def policy_fragment(self) -> iam.PrincipalPolicyFragment:
        return iam.PrincipalPolicyFragment(
            principal_json={'AWS': [self.arn]},
            conditions={'Bool': {'aws:MultiFactorAuthPresent': True}}
        )


class AssumeRoleStack(core.Stack):
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        user = iam.User(self, 'myuser')
        role = iam.Role(self, 'MFA_Admin_Role',
                        assumed_by=MFAAccountRootPrincipal(),
                        managed_policies=[iam.ManagedPolicy.from_aws_managed_policy_name('AdministratorAccess')],
                        max_session_duration=core.Duration.hours(8))
        user.add_to_policy(iam.PolicyStatement(actions=['sts:AssumeRole'], resources=[role.role_arn]))

Обратите внимание, что это не позволяет доверять учетным записям, кроме той, в которой определены пользователь и роль. Однако это можно легко решить, передав номер счета или экземпляр core.Environment в качестве аргумента инициализатору AssumeRoleStack. Передав это инициализатору MFAAccountRootPrincipal, вы можете вручную создать ARN корневого пользователя доверенной учетной записи.

person Milo    schedule 22.01.2020

Зачем изобретать собственные методы, если такая возможность уже существует в CDK?

role = iam.Role(
    self,
    "RoleWithCondition",
    description="Role insisting on MFA",
    assumed_by=iam.PrincipalWithConditions(
        principal=iam.AccountPrincipal("123456789012").grant_principal,
        conditions={
            "Bool":  {
                "aws:MultiFactorAuthPresent": True,
            },
        },
    ).grant_principal
)
person Phil    schedule 21.06.2021