为什么无法在两个 Amazon S3 存储桶间复制对象?

上次更新时间:2020 年 3 月 27 日

我正在尝试将对象从一个 Amazon Simple Storage Service (Amazon S3) 存储桶复制到另一个存储桶,但没有效果。我应该如何解决这个问题?

简短描述

要排查无法在存储桶间复制对象的问题,请检查以下事项:

  • 存储桶策略和 AWS Identity and Access Management (IAM) 策略
  • 对象所有权
  • AWS Key Management Service (AWS KMS) 加密
  • Amazon Simple Storage Service Glacier (Amazon S3 Glacier) 存储类
  • 存储桶上启用了申请方付款
  • AWS Organizations 服务控制策略
  • Amazon S3 的 Amazon Virtual Private Cloud (VPC) 终端节点的跨区域请求问题

解决方法

存储桶策略和 IAM 策略

要在存储桶间复制对象,必须确保配置了正确的权限。要在同一 AWS 账户的存储桶间复制对象,可以使用 IAM 策略设置权限。要在不同账户的存储桶间复制对象,必须同时在相关的 IAM 策略和存储桶策略上设置权限。

注意:有关如何修改存储桶策略的说明,请参阅如何添加 S3 存储桶策略? 有关如何修改 IAM 用户权限的说明,请参阅更改 IAM 用户的权限。有关如何修改 IAM 角色权限的说明,请参阅修改角色

确认以下所需的权限:

  • 至少,您的 IAM 身份(用户或角色)必须具有对源存储桶执行 s3:ListBucket 和 s3:GetObject 操作的权限。如果存储桶位于同一账户,则可以使用 IAM 身份的策略设置这些权限。如果存储桶位于不同账户,则要同时使用存储桶策略和 IAM 身份的策略设置这些权限。
  • 至少,您的 IAM 身份必须具有对目标存储桶执行 s3:ListBucket 和 s3:PutObject 操作的权限。如果存储桶位于同一账户,则可以使用 IAM 身份的策略设置这些权限。如果存储桶位于不同账户,则要同时使用存储桶策略和 IAM 身份的策略设置这些权限。
  • 查看相关存储桶策略和 IAM 策略,确认没有与所需权限冲突的显式拒绝语句。显式拒绝语句可覆盖允许语句
  • 对于特定操作,确认您的 IAM 身份具有在操作内执行所有必要操作的权限。例如,要运行命令 aws s3 cp,您需要具有执行 s3:GetObject 和 s3:PutObject 的权限。要运行包含 -recursive 选项的命令 aws s3 cp,您需要具有执行 s3:GetObject、s3:PutObject 和 s3:ListBucket 的权限。要运行命令 aws s3 sync,您需要具有执行 s3:GetObject、s3:PutObject 和 s3:ListBucket 的权限。
    注意:如果您正在使用 AssumeRole API 操作访问 Amazon S3,还必须验证是否已正确配置信任关系
  • 对于版本特定的操作,请确认您的 IAM 身份具有执行版本特定操作的权限。例如,如果要复制对象的特定版本,则除了 s3:GetObject 的权限外,您还需要 s3:GetObjectVersion 的权限。
  • 如果您复制对象拥有对象标签,则您的 IAM 身份必须具有源对象的 s3:GetObjectTagging 权限和目标存储桶中对象的 s3:PutObjectTagging 权限。
  • 查看相关存储桶策略和 IAM 策略,以确保Resource 元素具有正确的路径。对于存储桶级别权限,Resource 元素必须指向一个存储桶。对于对象级别权限,Resource 元素指向一个或多个对象。

例如,存储桶级别操作(如 s3:ListBucket)的策略语句必须在 Resource 元素中指定存储桶,如下所示:

"Resource": "arn:aws:s3:::awsexamplebucket"

对象级别操作(如 s3:GetObject 或 s3:PutObject)的策略语句必须在 Resource 元素中指定一个或多个对象,如下所示: 

"Resource": "arn:aws:s3:::awsexamplebucket/*"

对象所有权

如果存储桶策略具有正确的权限,并且您在存储桶间复制对象时仍存在问题,请检查拥有该对象的 AWS 账户。存储桶策略仅适用于存储桶拥有者拥有的对象。不同账户拥有的对象在其访问控制列表 (ACL) 上的权限可能存在冲突。

注意:跨账户复制 AWS 服务日志时,通常会出现对象所有权和 ACL 问题。服务日志示例包括 AWS CloudTrail 日志和 Elastic Load Balancing 访问日志。

请按照以下步骤查找拥有对象的账户:

1.    打开 Amazon S3 控制台

2.    导航到无法在存储桶间复制的对象。

3.    选择对象的权限选项卡。

4.    查看对象拥有者访问权限其他 AWS 账户的访问权限下的值:

  • 如果对象由您的账户所拥有,则对象拥有者访问权限下的规范 ID 包含 (您的 AWS 账户)
  • 如果对象由另一个账户所拥有,并且您有权访问该对象,则对象拥有者访问权限下的规范 ID 包含 (外部账户)其他 AWS 账户的访问权限下的规范 ID 包含 (您的 AWS 账户)
  • 如果对象由另一个账户所拥有,并且您无权访问该对象,则对象拥有者访问权限其他 AWS 账户的访问权限下的规范 ID 字段为空。

如果无法在存储桶间复制的对象由另一个账户所拥有,则对象拥有者可以执行下列操作之一:

  • 对象拥有者可以授予存储桶拥有者完全控制对象的权限。当存储桶拥有者拥有对象后,存储桶策略将应用于该对象。
  • 对象拥有者可以保留对象的所有权,但必须将 ACL 更改为您的使用案例所需的设置。

AWS KMS 加密

如果对象使用 AWS KMS 密钥加密,请确认您的 IAM 身份具有该密钥的正确权限。如果您的 IAM 身份和 AWS KMS 密钥属于同一账户,请确认密钥策略授予您的 IAM 身份执行以下操作的权限:

"Action": [
    "kms:Encrypt",
    "kms:Decrypt",
    "kms:ReEncrypt*",
    "kms:GenerateDataKey*",
    "kms:DescribeKey"
 ]

如果您的 IAM 身份和 AWS KMS 密钥属于不同账户,请确认密钥策略和您的 IAM 策略都授予所需的权限

有关更多信息,请参阅使用 AWS KMS 中的密钥策略AWS Key Management Service 的操作、资源和条件键

Amazon S3 Glacier 存储类

您无法从 Amazon S3 Glacier 存储类复制对象。您必须先从 Amazon S3 Glacier 还原对象,然后才可以复制对象。有关说明,请参阅如何还原已存档的 S3 对象?

存储桶上启用了申请方付款

如果源或目标存储桶已启用了申请方付款,且您正在尝试访问另一账户的存储桶,则请验证您的请求是否包含正确的申请方付款参数

  • 对于 AWS 命令行界面 (AWS CLI) 命令,包含 --request-payer 选项。
  • 对于 GET、HEAD 和 POST 请求,包含 x-amz-request-payer : requester
  • 对于签名 URL,包含 x-amz-request-payer=requester

AWS Organizations 服务控制策略

如果您使用的是 AWS Organizations,在检查服务控制策略以确保允许访问 Amazon S3。

例如,当您尝试访问 Amazon S3 时,以下策略将导致 403 禁止错误,因为其会明确拒绝访问: 

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Action": "S3:*",
            "Resource": "*"
        }
    ]
}

有关 AWS Organizations 功能的更多信息,请参阅启用组织中的所有功能

Amazon S3 的 VPC 终端节点的跨区域请求问题

Amazon S3 的 VPC 终端节点目前不支持跨区域请求。例如,如果区域 A 中的 Amazon Elastic Compute Cloud (Amazon EC2) 实例在其关联的路由表中配置了 VPC 终端节点,则该实例无法将区域 B 中的对象复制到区域 A 中的存储桶。不过,您会收到类似于以下内容的错误消息:

“An error occurred (AccessDenied) when calling the CopyObject operation: VPC endpoints do not support cross-region requests”

要排查此跨区域请求问题,您可以:

  • 从路由表删除 VPC 终端节点。如果您删除 VPC 终端节点,则实例必须能连接到 Internet。
  • 从另一个不使用 VPC 终端节点的实例,或者从区域 A 或区域 B 外的实例运行复制命令。
  • 如果您必须使用 VPC 终端节点,则发送 GET 请求以将对象从源存储桶复制到 EC2 实例。然后,发送 PUT 请求以将对象从 EC2 实例复制到目标存储桶。 

这篇文章对您有帮助吗?

我们可以改进什么?


需要更多帮助?