亚马逊AWS官方博客

如何将亚马逊 AWS S3 存储桶的访问权限到一个特定 IAM 角色

译自 Chris Craig 发布在 AWS 英文官方博客的文章 | 我是 AWS 的一名云支持工程师,客户经常问我如何将 Amazon S3 存储桶访问权限限制到特定的 AWS Identity and Access Management (IAM) 角色。通常,他们会尝试以对待 IAM 用户的相同方式执行此操作:使用存储桶策略显式 Deny 不想授予访问权限的所有 Principals(用户和角色)。这种方法的缺点是需要维护存储桶策略。如果将一个新的 IAM 用户添加到账户,并且其 Action“s3:*”,则该用户将获得访问该存储桶的权限。您可以反转逻辑并在存储桶策略的 Deny 语句中利用 NotPrincipal 元素,而不必指定要阻止其访问的用户列表。此元素会为其值中未列出的任何用户创建一个显式的 Deny

但事实证明,这种反转逻辑的方法在处理 IAM 角色时存在问题,因为这种角色的 Principal 值包含两个 Amazon 资源名称 (ARN),即 role ARN 和 assumed-role ARN。 role ARN 是 IAM 角色本身的标识符, assumed-role ARN 则用于标识日志中的角色会话。在使用 NotPrincipal 元素时,您必须同时包含两个 ARN,此方法才能正常工作,其中第二个 ARN 应包含一个变量名称。通常,您应指定一个通配符,用于表示变量字符串,但不允许在 PrincipalNotPrincipal 元素中指定此通配符。在本博文中,我会向您展示如何使用 Conditions(而非 NotPrincipal 元素),将 S3 存储桶的访问权限限制到一个账户内的特定 IAM 角色或用户。即便相同账户中的另一个用户拥有 Admin 策略或带有 s3:* 的策略,只要未被显式列出,也同样会被拒绝。例如,您可以使用此方法配置存储桶,以供 Auto Scaling 组内的实例访问。您还可以使用此方法,限制对具有高级别安全需求的存储桶的访问。

解决方案概述

本文所述的解决方案使用存储桶策略来管理对 S3 存储桶的访问 – 即使实体可以访问完整的 S3 API。下图展示了此解决方案如何应用于同一账户内的存储桶。

展示此解决方案如何应用于同一账户内的存储桶的图解

  1. IAM 用户的策略和角色的用户策略授予对 “s3:*” 的访问权限。
  2. S3 存储桶策略将访问权限限制为仅限该角色。
  3. IAM 用户和角色都可以访问该账户中的存储桶。该角色可以访问这两个存储桶,但用户只能访问没有附加存储桶策略的存储桶。即使角色和用户都拥有完整的 “s3:*” 权限,存储桶策略仍会拒绝任何不具有该角色的人员访问该存储桶。

跨账户方法的主要不同之处在于,每个存储桶必须附加存储桶策略。下图展示了这种方法如何应用于跨账户部署场景。

展示此解决方案如何应用于跨账户部署场景的图解

  1. IAM 角色的用户策略和存储桶账户中的 IAM 用户策略均授予对“s3:*”的访问权限
  2. 如果任何人的 user:id 与角色的相应值不同,并且策略定义了允许角色对存储桶执行哪些操作,则存储桶策略会拒绝其访问。
  3. 存储桶策略允许从其他账户访问角色。
  4. IAM 用户和角色可以在存储桶策略中没有 Deny 的情况下访问存储桶。角色可以同时访问这两个存储桶,因为 Deny 仅适用于其 user:id 不等于角色的相应值的主体。

了解 NotPrincipal 元素及其用法

您可以使用 IAM 或 S3 存储桶策略的 NotPrincipal 元素,仅限特定用户组访问资源。此元素允许您阻止未在其值数组中定义的所有用户,即使他们自己的 IAM 用户策略中具有 Allow 也是如此。因此,如果您有一个用户应该可以访问除 S3 中的某个存储桶以外的所有存储桶,则可以在该存储桶本身上进行此定义,而无需编辑用户的 IAM 策略堆栈。

但对于 IAM 角色来说,这要更为复杂,因为角色是由 Principal 中的两个 ARN 定义的:role ARN 和 assumed-role ARN。role ARN (arn:aws:iam::ACCOUNTNUMBER:role/ROLE-NAME) 是静态的,独立于角色会话发起者。(在本文中,切记将 placeholder information 替换为您自己的账户信息。)assumed-role ARN (arn:aws:sts::ACCOUNTNUMBER:assumed-role/ROLE-NAME/ROLE-SESSION-NAME) 将因为角色会话名称定义的内容而异。对于具有一个角色的用户发出的 API 调用,您可以通过查看 AWS CloudTrail 条目中的以下 Identity 元素来了解此信息。

{
  "type": "AssumedRole",
  "principalId": "AROAJI4AVVEXAMPLE:ROLE-SESSION-NAME",
  "arn": "arn:aws:sts::ACCOUNTNUMBER:assumed-role/ROLE-NAME/ROLE-SESSION-NAME",
  "accountId": "ACCOUNTNUMBER",
  "accessKeyId": "ASIAEXAMPLEKEY",
  "sessionContext": {
    "attributes": {
      "mfaAuthenticated": "false",
      "creationDate": "XXXX-XX-XXTXX:XX:XXZ"
    },
    "sessionIssuer": {
      "type": "Role",
      "principalId": "AROAJI4AVV3EXAMPLEID",
      "arn": "arn:aws:iam::ACCOUNTNUMBER:role/ROLE-NAME",
      "accountId": "ACCOUNTNUBMER",
      "userName": "ROLE-SESSION-NAME"
    }
  }
}

在此 Identity 元素中,您可以看到 role ARN 和 assumed-role ARN。根据承担角色的用户不同,ROLE-SESSION-NAME 也可能会有所变化。principalId 值也包含此信息,但采用可以在存储桶策略的 Principal 元素以外使用的方式设置格式。在编写存储桶策略时,我会使用这些信息。

向特定角色授予同账户存储桶访问权限

在从同一账户访问存储桶时,在大多数情况下不必使用存储桶策略。这是因为存储桶策略定义了已由用户的直接 IAM 策略授予的访问权限。S3 存储桶策略通常用于跨账户访问,但您也可以使用它们,通过显式 Deny 来限制访问,这将应用于所有主体,无论是与存储桶位于相同账户中还是不同账户中的主体。

每个 IAM 实体(用户、组或角色)都有一个已定义的 aws:userid 变量。您需要在存储桶策略中使用此变量,才能在条件元素内以例外的形式指定角色或用户。assumed-roleaws:userId 值定义为 UNIQUE-ROLE-ID:ROLE-SESSION-NAME(例如 AROAEXAMPLEID:userdefinedsessionname)。

要获得 IAM 角色的 AROAEXAMPLEID,请执行以下操作:

  1. 务必安装 AWS CLI,并打开一个命令提示符或 shell。
  2. 运行以下命令:aws iam get-role -–role-name ROLE-NAME
  3. 在输出中,查找以 AROA 开头的 RoleId 字符串。您将在存储桶策略中使用它来将存储桶访问权限的范围仅限于此角色。

在前述 CloudTrail 代码示例中,此 ID 为 principalId 元素。此元素的值十分重要,因为 AWS 策略变量也可以在 IAM 策略中作为字符串进行检查。您不必在 NotPrincipal 元素中指定 roleassumed-role ARN,而是可以将 StringNotLike 条件中的 aws:userId 值与通配符字符串一起使用。在 aws:userId 值内,您还需要添加账户的 Root 用户,以便在删除定义的角色时,不会致使存储桶完全无法访问。Root 账户的 userId 是账户编号。

使用您刚刚通过 AWS CLI 检索的 AROAEXAMPLEID,即可创建条件逻辑,让存储桶策略将存储桶访问权限的范围仅限于访问存储桶时使用此角色的用户。使用条件逻辑而非 NotPrincipal 元素可支持使用通配符字符串,从而允许接受任何角色会话名称。

现在您已获得要允许其访问的角色 ID,接下来需要阻止与存储桶处于相同账户内的其他用户的访问。阻止未使用 IAM 角色或 Root 账户凭证的用户访问存储桶及其对象的策略如下所示。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::MyExampleBucket",
        "arn:aws:s3:::MyExampleBucket/*"
      ],
      "Condition": {
        "StringNotLike": {
          "aws:userId": [
            "AROAEXAMPLEID:*",
            "111111111111"
          ]
        }
      }
    }
  ]
}

您也可以对 IAM 用户使用相同的策略。IAM 用户拥有一个以 AIDA 开头的唯一 ID,您可以将此 ID 用于此用途。要查找此唯一 ID,请执行以下操作:

  1. 安装 AWS CLI 后,打开命令提示符或 shell。
  2. 运行命令:aws iam get-user -–user-name USER-NAME
  3. 在输出中,查找以 AIDAEXAMPLEID 开头的 userId 字符串。

找到 userId 字符串之后,您可以将其置于“aws:userId”条件数组中,如以下示例所示。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::MyExampleBucket",
        "arn:aws:s3:::MyExampleBucket/*"
      ],
      "Condition": {
        "StringNotLike": {
          "aws:userId": [
            "AROAEXAMPLEID:*",
            "AIDAEXAMPLEID",
            "111111111111"
          ]
        }
      }
    }
  ]
}

向特定 IAM 角色授予跨账户存储桶访问权限

在上一节中,我向您展示了如何将 S3 存储桶访问权限限于同一个账户内的特定 IAM 角色或用户。现在,我将向您展示如何将访问权限限制到另一账户中的特定用户和角色。向 IAM 用户或角色授予跨账户存储桶访问权限时,您必须定义允许 IAM 用户或角色通过该访问权限执行哪些操作。在先前的 AWS 安全性博客中,Jim Scharf 撰文介绍了允许 IAM 实体通过 CLI/API 和控制台访问存储桶所需的权限。利用之前这篇博文中提供的信息,CLI/API 级访问存储桶策略应如下所示。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111111111111:role/ROLENAME"
            },
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::MyExampleBucket"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111111111111:role/ROLENAME"
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::MyExampleBucket/*"
        },
        {
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::MyExampleBucket",
                "arn:aws:s3:::MyExampleBucket/*"
            ],
            "Condition": {
                "StringNotLike": {
                    "aws:userId": [
                        "AROAEXAMPLEID:*",
                        "111111111111"
                    ]
                }
            }
        }
    ]
}

以下策略显示了控制台级访问所需的服务操作,例如与控制台的 IAM 切换角色功能配合使用的操作。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111111111111:role/ROLENAME"
            },
            "Action": [
                "s3:ListAllMyBuckets",
                "s3:GetBucketLocation"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111111111111:role/ROLENAME"
            },
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::MyExampleBucket"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111111111111:role/ROLENAME"
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::MyExampleBucket/*"
        },
        {
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::MyExampleBucket",
                "arn:aws:s3:::MyExampleBucket/*"
            ],
            "Condition": {
                "StringNotLike": {
                    "aws:userId": [
                        "AROAEXAMPLEID:*",
                        "111111111111"
                    ]
                }
            }
        }
    ]
}

要向其他账户中的 IAM 用户授予 API/CLI 访问权限,您需要将 IAM 用户的 AIDAEXAMPLEID 添加到“aws:userId”条件,就像我们上一节所做的那样。除了“aws:userId”条件之外,您还需要将 IAM 用户的完整 ARN 添加到这些策略的 Principal 元素。请注意,您不能向 IAM 用户授予跨账户控制台访问权限,因为该用户需要具备目标账户中的角色,但您可以通过 API/CLI 授予对存储桶的访问权限。具体如下所示。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": [
                {
                    "AWS": [
                        "arn:aws:iam::222222222222:role/ROLENAME",
                        "arn:aws:iam::222222222222:user/USERNAME"
                    ]
                }
            ],
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::MyExampleBucket"
        },
        {
            "Effect": "Allow",
            "Principal": [
                {
                    "AWS": [
                        "arn:aws:iam::222222222222:role/ROLENAME",
                        "arn:aws:iam::222222222222:user/USERNAME"
                    ]
                }
            ],
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::MyExampleBucket/*"
        },
        {
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::MyExampleBucket",
                "arn:aws:s3:::MyExampleBucket/*"
            ],
            "Condition": {
                "StringNotLike": {
                    "aws:userId": [
                        "AROAEXAMPLEID:*",
                        "AIDAEXAMPLEID",
                        "111111111111"
                    ]
                }
            }
        }
    ]
}

除了在存储桶策略中包含角色权限之外,您还需要在 IAM 用户或角色的用户策略中定义这些权限。这些权限可以添加到客户托管的策略附加到 IAM 控制台中的角色或用户,详见以下策略文档。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListAllMyBuckets",
        "s3:GetBucketLocation"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::MyExampleBucket"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": "arn:aws:s3:::MyExampleBucket/*"
    }
  ]
}

按照本文中的指导,即使用户拥有 Admin 策略或带有 s3:* 的策略,也可以将 S3 存储桶访问权限限于本地账户内和不同账户中的特定 IAM 角色或用户。这种逻辑有多种应用,各种使用案例的要求可能会有所不同。例如,您可以使用此方法设置存储桶,以供 Auto Scaling 组内的实例访问。如同在包含个人记录和账户信息的存储桶中一样,您还可以使用此方法限制对具有高级别安全需求的存储桶的访问。务必牢记,最好始终仅将权限授予执行必要任务所需的资源。

如果您对本博文有任何评论,欢迎在下方的“评论”部分提交。如果您有任何疑问,请在 IAM 论坛中开启一个新话题。

– Chris