为任何 Cloudfront 源访问身份添加 S3 存储桶策略

Adding S3 Bucket Policy for any Cloudfront Origin Access Idenity

将多个 CloudFronts 与一个存储桶一起使用效果不佳,因为我可以在 CloudFormation 中授予访问权限的唯一方法是向模板添加 BucketPolicy。对于多个 apps/templates,这将始终覆盖整个存储桶策略以添加 S3 Origin Access Identity.

的权限
    S3OriginIdentity:
        Type: 'AWS::CloudFront::CloudFrontOriginAccessIdentity'
        Properties:
          CloudFrontOriginAccessIdentityConfig:
            Comment: S3 Origin Identity
    S3OriginIdentityS3ReadPolicy:
      Type: "AWS::S3::BucketPolicy"
      Properties:
        Bucket: my-bucket
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Sid: my-cloudfront-read-access
            Action:
              - s3:GetObject
            Effect: Allow
            Principal:
              CanonicalUser:
                Fn::GetAtt: S3OriginIdentity.S3CanonicalUserId
            Resource: arn:aws:s3:::my-bucket/path/*

为了解决这个问题,我想到我可以在存储桶策略中使用一个条件来授予对任何 Cloudfront 身份的读取访问权限。从 the documentation 看来 aws:SourceArnaws:PrincipalArn 应该可以解决问题。这是我尝试过的示例策略。

{
    "Version": "2012-10-17",
    "Statement": [{
        "Sid": 1,
        "Action": [
            "s3:GetObject"
        ],
        "Effect": "Allow",
        "Resource": "arn:aws:s3:::my-bucket/*",
        "Condition": {
            "ArnLike": {
                "aws:SourceArn": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity*"
            }
        },
        "Principal": "*"
    }]
}

当我使用aws:PrincipalArn时,控制台甚至不让我保存它(一个通用的Access Denied),但我用上面的aws:SourceArn版本试过了,它没有给访问权限。我究竟做错了什么?或者有更好的方法吗?

aws:SourceArn 被列为 "is available for only some services" 并且不是指主体而是指作为操作源的资源,例如 SNS 主题SQS 队列策略或 Macie 策略中的 S3 存储桶。

从这个角度来看,我认为它在这里不适用,并且 s3:GetObject 没有提及它的可能性 -- 这个操作 only appears to support 少数非-全局条件键,所有这些都是特定于 S3 的:s3:ExistingObjectTag/<key>s3:authtypes3:signatureages3:signatureversions3:x-amz-content-sha256

aws:PrincipalArn 是一个全局条件键,所以看起来它可能有效,但我建议你很幸运它没有。如果有,那么您会将您的存储桶暴露给所有 AWS 账户中所有 CloudFront 分配的所有源访问身份。 "access denied" 错误是意外的,但可能源于您缺乏对 arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity* 的权限这一事实,这是有道理的,因为它不在您的帐户中——OAI 由 CloudFront 分配给您的帐户但严格来说,它们不是您的 AWS 账户的一部分——它们是一个外部实体。¹ 鉴于此,似乎不太可能在政策声明中表达 "all of my OAIs"。

我建议让多个 CloudFront 分布访问单个存储桶的最直接方法是创建单个 OAI 并配置您的多个 CloudFront 分布全部使用它。单个 OAI 和单个存储桶策略授权不仅笨拙,而且无法扩展——存储桶策略的大小限制为 ~20KB。

但如果那是你想要的方向,似乎可以使用 Lambda-Backed Custom Resource。这里的想法是创建一个 Lambda 函数,该函数将 编辑 存储桶策略并将新的 OAI 附加到包含已添加的 OAI 的 Principal 数组。 (存储桶策略中的 Principal 可以是标量或数组)。这样做的危险在于它是一个微妙的操作——没有存储桶策略的并发保护,所以如果这些堆栈中的两个试图同时有效地修改策略(获取、编辑、放置),那么其中一个将看到它的更新被忽略了,因为它们被另一个覆盖了。 (最终按此顺序进行的并发操作——读取#1、修改#1、读取#2、修改#2、写入#1、写入#2——导致写入#1 的更改丢失,因为写入#2 存储了数据这是基于阅读 #2,与阅读 #1 相同。)


¹此断言的理由:即使 x-amz-acl: bucket-owner-full-control 应用于对象,OAI 也无法访问由对您的存储桶具有写入权限的不同 AWS 账户创建的存储桶中的对象:"If another AWS account uploads files to your bucket, that account is the owner of those files. Bucket policies only apply to files that the bucket owner owns. This means that if another account uploads files to your bucket, the bucket policy that you created for your OAI will not be evaluated for those files." 如果您的存储桶策略明确向不同账户中的 IAM 用户授予读取访问权限,则情况完全相同——当还创建对象时,存储桶策略无法将读取权限委托给同一账户外的用户来自帐户外部的用户。如果存储桶策略允许,您帐户内的用户可以访问此类对象。

我知道这是一个老问题,但我 运行 遇到过类似的问题。

我至少可以阐明使用 aws:PrincipalArn 时出现的 AccessDenied 错误。在我的例子中,这是因为我的 S3 存储桶启用了块 public 策略,并且 aws:PrincipalArn 条件不足以成为非 public 委托人。可以在此处找到所需的条件 https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-control-block-public-access.html#access-control-block-public-access-policy-status.

我无法解决的问题是,在 Terraform 中似乎无法查询以前创建的 OAI。因此,如果我在一个模块中创建 OAI、S3 存储桶和策略,我以后就无法为 CloudFront 分发版获取 OAI。