我使用 S3 REST API 终端节点作为我的 CloudFront 分配源。为什么我会收到“403 访问被拒绝”错误?

上次更新时间:2020 年 5 月 28 日

我使用 Amazon Simple Storage Service (Amazon S3) 存储桶作为我的 Amazon CloudFront 分配源。我使用 S3 REST API 终端节点作为源域名。为什么 CloudFront 从 Amazon S3 返回“403 访问被拒绝”错误?

简短描述

要解决“访问被拒绝”错误,您必须知道分配源的源域名是 S3 网站终端节点还是 S3 REST API 终端节点。按照以下步骤来确定终端节点类型:

  1. 打开 CloudFront 控制台
  2. 选择您的 CloudFront 分配,然后选择分配设置
  3. 选择源和源组选项卡。
  4. 查看源域名和路径下的域名。然后,根据域名的格式确定终端节点类型。

REST API 终端节点使用以下格式:

AWSDOC-EXAMPLE-BUCKET.s3.amazonaws.com

静态网站终端节点使用以下格式:

AWSDOC-EXAMPLE-BUCKET.s3-website-us-east-1.amazonaws.com

如果您的分配使用 S3 静态网站终端节点,请参阅我使用 S3 网站终端节点作为我的 CloudFront 分配源。为什么我会收到“403 访问被拒绝”错误?

如果您的分配使用 REST API 终端节点,请确认您的配置是否符合以下要求,以免出现“访问被拒绝”错误:

  • 如果您未配置源访问身份 (OAI),则对象必须可以公开访问,或可以使用 AWS 签名版本 4 请求。
  • 存储桶中的对象无法使用 AWS Key Management Service (AWS KMS) 加密。
  • S3 存储桶策略必须允许访问 s3:GetObject。
  • 如果存储桶策略授予访问权限,则拥有 S3 存储桶的 AWS 账户还必须拥有该对象。
  • 请求的对象必须存在于 S3 存储桶中。
  • 如果客户端请求分配的根目录,则您必须定义默认的根对象。
  • 如果您配置了 OAI,则 S3 存储桶策略必须包含 OAI。
  • 如果您没有配置 OAI,则必须禁用 Amazon S3 阻止公开访问设置。

解决方法

如果您未配置 OAI,则对象必须可以公开访问,或可以使用 AWS 签名版本 4 请求

如果您未配置 OAI,则使用 REST API 终端节点的分配仅支持公开对象,或仅支持使用 AWS 签名版本 4 身份验证请求的对象。

要确定 S3 存储桶中的对象是否可以公开访问,请在 Web 浏览器中打开 S3 对象的 URL。或者,您也可以对 URL 运行 curl 命令。

下面是 S3 对象的示例 URL:

http://AWSDOC-EXAMPLE-BUCKET.s3-website-us-east-1.amazonaws.com/index.html

如果 Web 浏览器或 curl 命令返回“访问被拒绝”错误,则该对象不可公开访问。如果对象不可公开访问,请使用以下某项配置:

存储桶中的对象无法进行 AWS KMS 加密

CloudFront 分配不支持 AWS KMS 加密对象。您必须从要使用分配提供服务的 S3 对象中删除 KMS 加密。

注意:您可以使用 AES-256 加密您的对象,而不是使用 AWS KMS 加密。

使用下述方法之一来检查您存储桶中的对象是否为 KMS 加密对象:

  • 使用 Amazon S3 控制台查看对象属性。查看加密对话框。如果已选中 AWS-KMS,则对象已进行 KMS 加密。
  • 或者,您可以使用 AWS 命令行界面 (AWS CLI) 运行head-object 命令。如果命令返回的 ServerSideEncryptionaws:kms,则表明对象已进行 KMS 加密。

要使用 Amazon S3 控制台更改对象的加密设置,请参阅如何为 S3 对象添加加密?

要使用 AWS CLI 更改对象的加密设置,您必须先确认该对象的存储桶没有默认加密。如果存储桶没有默认加密,请运行以下 AWS CLI 命令,通过将对象复制到其本身来删除对象的加密。

警告:将对象复制到其本身将会删除 storage-classwebsite-redirect-location 的设置。要在新对象中保留这些设置,请务必明确在复制请求中指定 storage-classwebsite-redirect-location 的值。

aws s3 cp s3://AWSDOC-EXAMPLE-BUCKET/index.html s3://AWSDOC-EXAMPLE-BUCKET/index.html

S3 存储桶策略必须允许访问 s3:GetObject

要使用具有 S3 REST API 终端节点的分配,您的存储桶策略必须允许公共用户或 CloudFront 的 OAI 访问 s3:GetObject。

即使您的存储桶策略中有 s3:GetObject 的显式允许语句,也请确认没有冲突的显式拒绝语句。显式拒绝语句始终覆盖显式允许语句。

请按照以下步骤查看您的 s3:GetObject 存储桶策略:

1.    从 Amazon S3 控制台打开您的 S3 存储桶。

2.    选择权限选项卡。

3.    选择存储桶策略

4.    查看存储桶策略,查找包含 "Action": "s3:GetObject""Action": "s3:*" 的语句。

在以下示例策略中,有一个允许语句会授予 CloudFront OAI 访问 s3:GetObject. 的权限。还有一个允许语句会授予 s3:GetObject 的公开访问权限。但是,有一个阻止访问 s3: GetObject 的显式拒绝语句,除非请求来自特定 Amazon Virtual Private Cloud (Amazon VPC)。

{
  "Version": "2008-10-17",
  "Id": "PolicyForCloudFrontPrivateContent",
  "Statement": [{
      "Sid": "Allow-OAI-Access-To-Bucket",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity EAF5XXXXXXXXX"
      },
      "Action": "s3:GetObject",
      "Resource": [
        "arn:aws:s3:::AWSDOC-EXAMPLE-BUCKET/*"
      ]
    },
    {
      "Sid": "Allow-Public-Access-To-Bucket",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": [
        "arn:aws:s3:::AWSDOC-EXAMPLE-BUCKET/*"
      ]
    },
    {
      "Sid": "Access-to-specific-VPCE-only",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": [
        "arn:aws:s3:::AWSDOC-EXAMPLE-BUCKET/*"
      ],
      "Condition": {
        "StringNotEquals": {
          "aws:sourceVpce": "vpce-1a2b3c4d"
        }
      }
    }
  ]
}

5.    修改存储桶策略,以删除或编辑阻止 CloudFront OAI 访问或公开访问 s3:GetObject 的语句。

注意:CloudFront 可将“访问被拒绝”错误的结果缓存最多 5 分钟。从存储桶策略删除拒绝语句后,您可以在您的分配上运行失效以从缓存中删除对象。

如果存储桶策略授予访问权限,则拥有 S3 存储桶的 AWS 账户还必须拥有该对象

对于应用于外部账户或服务的存储桶策略(例如,公开访问或 OAI),拥有存储桶的 AWS 账户也必须拥有这些对象。存储桶或对象归创建该存储桶或对象的 AWS Identity and Access Management (IAM) 身份所属的账户所有。

注意:对象所有权要求适用于存储桶策略授予的访问权限。它不适用于对象的访问控制列表 (ACL) 授予的访问权限。

按照以下步骤检查存储桶和对象的拥有者是否相同:

1.    运行以下 AWS CLI 命令以获得存储桶拥有者的 S3 规范 ID:

aws s3api list-buckets --query Owner.ID

2.    运行以下命令以获得对象拥有者的 S3 规范 ID。

注意:此示例显示的是单个对象,但您可以使用此列表命令来检查多个对象。

aws s3api list-objects --bucket AWSDOC-EXAMPLE-BUCKET --prefix index.html

3.    如果规范 ID 不匹配,则该存储桶和对象的拥有者不同。

注意:您也可以使用 Amazon S3 控制台来检查存储桶和对象拥有者。拥有者可从相应存储桶或对象的权限选项卡中找到。

按照以下步骤将对象的拥有者更改为存储桶拥有者:

1.    从对象拥有者的 AWS 账户,运行此命令以检索分配给对象的访问控制列表 (ACL) 权限:

aws s3api get-object-acl --bucket AWSDOC-EXAMPLE-BUCKET --key object-name

2.    如果对象具有 bucket-owner-full-control ACL 权限,则跳至步骤 3。如果对象没有 bucket-owner-full-control ACL 权限,则通过对象拥有者的账户运行以下命令:

aws s3api put-object-acl --bucket AWSDOC-EXAMPLE-BUCKET --key object-name --acl bucket-owner-full-control

3.    从存储桶拥有者的账户,运行此命令以通过将对象复制到其本身来更改对象的拥有者:

aws s3 cp s3://AWSDOC-EXAMPLE-BUCKET/index.html s3://AWSDOC-EXAMPLE-BUCKET/index.html

请求的对象必须存在于存储桶中

如果用户没有 s3:ListBucket 权限,则用户会因对象缺失遇到“访问被拒绝”错误,而不是“404 未找到”错误。运行 head-object AWS CLI 命令以检查存储桶中是否存在某对象。

注意:请确保发送到 CloudFront 的对象请求与 S3 对象名称完全匹配。S3 对象名称区分大小写。如果请求没有正确的对象名称,则 Amazon S3 响应就会像对象缺失一样。要确定 CloudFront 正在从 Amazon S3 请求哪个对象,可使用服务器访问日志记录

如果存储桶中存在该对象,则“访问被拒绝”错误不会屏蔽“404 未找到”错误。验证其他配置要求以解决“访问被拒绝”错误。

如果存储桶中不存在该对象,则“访问被拒绝”错误将屏蔽“404 未找到”错误。解决与对象缺失相关的问题。

注意:我们建议不要启用 s3:ListBucket 公开访问权限,因为这不符合安全最佳实践。

如果客户端请求分配的根目录,则您必须定义默认的根对象

如果您的分配没有定义默认的根对象,并且请求者没有 s3:ListBucket 访问权限,则请求者会收到“访问被拒绝”错误。请求者在请求分配的根目录时会收到此错误,而不是“404 未找到”错误。

要定义默认的根对象,请参阅指定默认根对象

注意:我们建议不要启用 s3:ListBucket 公开访问权限,因为这不符合安全最佳实践。

如果您配置了 OAI,则 S3 存储桶策略必须包含 OAI

如果您向 CloudFront 分配添加了 OAI,则还必须在 S3 存储桶策略中添加 OAI 允许语句。

要确认您的存储桶策略是否允许 OAI,请在 Amazon S3 控制台中打开 S3 存储桶。然后,选择权限选项卡并查看存储桶策略。以下是 OAI 的允许语句示例:

{
  "Sid": "1",
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity EAF5XXXXXXXXX"
  },
  "Action": "s3:GetObject",
  "Resource": "arn:aws:s3:::AWSDOC-EXAMPLE-BUCKET/*"
}

要使用 CloudFront 控制台更新存储桶策略,请按照下列步骤操作:

  1. 打开 CloudFront 控制台,然后选择您的分配。
  2. 选择源和源组选项卡。
  3. 选择 S3 源,然后选择编辑
  4. 对于限制存储桶访问,请选择
  5. 对于源访问身份,请选择现有身份或创建一个新身份。
  6. 对于授予存储桶读取权限,请选择是,更新存储桶策略
  7. 选择是,编辑

如果您没有配置 OAI,则必须禁用 Amazon S3 阻止公开访问设置

如果分配未使用 OAI 和使用 AWS 签名版本 4 请求的对象,则使用 REST API 终端节点的分配仅支持公共对象。这意味着,您必须确认存储桶没有应用任何 Amazon S3 阻止公开访问设置。这些设置会覆盖允许公开访问的权限。阻止公开访问设置可以应用于各个存储桶或 AWS 账户。